home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH7.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  85KB  |  4,098 lines

  1. /* MS-DOS SHELL - Internal Command Processing
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions.  The
  7.  * code for the test (dotest) command was based on code written by
  8.  * Erik Baalbergen.  The following copyright conditions apply:
  9.  *
  10.  * 1.  Redistribution and use in source and binary forms are permitted
  11.  *     provided that the above copyright notice is duplicated in the
  12.  *     source form and the copyright notice in file sh6.c is displayed
  13.  *     on entry to the program.
  14.  *
  15.  * 2.  The sources (or parts thereof) or objects generated from the sources
  16.  *     (or parts of sources) cannot be sold under any circumstances.
  17.  *
  18.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh7.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
  19.  *
  20.  *    $Log: sh7.c,v $
  21.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  22.  *    BETA 215 Fixes and 2.1 Release
  23.  *
  24.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  25.  *    214 Beta test updates
  26.  *
  27.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  28.  *    Beta 213 Updates
  29.  *
  30.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  31.  *    Beta 212 Baseline
  32.  *
  33.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  34.  *    211 Beta updates
  35.  *
  36.  *    Revision 2.0  1992/05/07  21:33:35  Ian_Stewartson
  37.  *    MS-Shell 2.0 Baseline release
  38.  *
  39.  */
  40.  
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #include <stdio.h>
  44. #include <signal.h>
  45. #include <errno.h>
  46. #include <setjmp.h>
  47. #include <ctype.h>
  48. #include <string.h>
  49. #include <unistd.h>
  50. #include <stdlib.h>
  51. #include <fcntl.h>
  52. #include <limits.h>
  53. #include <dirent.h>
  54. #include <stdarg.h>
  55. #include <time.h>
  56. #ifdef OS2
  57. #define INCL_DOSSESMGR
  58. #define INCL_DOSPROCESS
  59. #define INCL_DOSSIGNALS
  60. #include <os2.h>
  61. #else
  62. #include <dos.h>
  63. #endif
  64. #include "sh.h"
  65.  
  66. #define    SECS        60L
  67. #define    MINS        3600L
  68. #define IS_OCTAL(a)    (((a) >= '0') && ((a) <= '7'))
  69.  
  70. /* Definitions for echo and print */
  71.  
  72. #define ECHO_ESCAPE    0x01
  73. #define ECHO_NO_EOL    0x02
  74. #define ECHO_HISTORY    0x04
  75.  
  76. /* Definitions for test */
  77.  
  78. #define END_OF_INPUT    0
  79. #define FILE_READABLE    1
  80. #define FILE_WRITABLE    2
  81. #define FILE_REGULAR    3
  82. #define FILE_DIRECTRY    4
  83. #define FILE_NONZERO    5
  84. #define FILE_TERMINAL    6
  85. #define STRING_ZERO    7
  86. #define STRING_NONZERO    8
  87. #define STRING_EQUAL    9
  88. #define STRING_NOTEQUAL    10
  89. #define NUMBER_EQUAL    11
  90. #define NUMBER_NOTEQUAL    12
  91. #define NUMBER_EQ_GREAT    13
  92. #define NUMBER_GREATER    14
  93. #define NUMBER_EQ_LESS    15
  94. #define NUMBER_LESS    16
  95. #define UNARY_NOT    17
  96. #define BINARY_AND    18
  97. #define BINARY_OR    19
  98. #define LPAREN        20
  99. #define RPAREN        21
  100. #define OPERAND        22
  101. #define FILE_EXECUTABLE    23
  102. #define FILE_USER    24
  103. #define FILE_GROUP    25
  104. #define FILE_TEXT    26
  105. #define FILE_BLOCK    27
  106. #define FILE_CHARACTER    28
  107. #define FILE_FIFO    29
  108. #define FILE_NEWER    30
  109. #define FILE_OLDER    31
  110. #define STRING_LESS    32
  111. #define STRING_GREATER    33
  112. #define FILE_EXISTS    34
  113. #define TEST_OPTION    35
  114. #define FILE_SYMBOLIC    36
  115. #define FILE_OWNER    37
  116. #define FILE_GROUPER    38
  117. #define FILE_SOCKET    39
  118. #define FILE_EQUAL    40
  119.  
  120. #define UNARY_OP    1
  121. #define BINARY_OP    2
  122. #define B_UNARY_OP    3
  123. #define B_BINARY_OP    4
  124. #define PAREN        5
  125.  
  126. /* This is the list of operators and the conversion values */
  127.  
  128. static struct TestOperator {
  129.     char    *OperatorName;
  130.     short     OperatorID;
  131.     short     OperatorType;
  132. } ListOfTestOperators[] = {
  133.  
  134. /* These two entries are modified depending on the test program. The
  135.  * alternative values are shown in the following comment.
  136.  */
  137.  
  138.     {"-a",    FILE_EXISTS,        UNARY_OP},
  139.     /* {"-a",    BINARY_AND,        B_BINARY_OP}, */
  140.     {"-o",    TEST_OPTION,        UNARY_OP},
  141.     /* {"-o",    BINARY_OR,        B_BINARY_OP}, */
  142.  
  143. /* Add new entries after here */
  144.  
  145.     {"-r",    FILE_READABLE,        UNARY_OP},
  146.     {"-w",    FILE_WRITABLE,        UNARY_OP},
  147.     {"-x",    FILE_EXECUTABLE,    UNARY_OP},
  148.     {"-f",    FILE_REGULAR,        UNARY_OP},
  149.     {"-d",    FILE_DIRECTRY,        UNARY_OP},
  150.     {"-s",    FILE_NONZERO,        UNARY_OP},
  151.     {"-t",    FILE_TERMINAL,        UNARY_OP},
  152.     {"-z",    STRING_ZERO,        UNARY_OP},
  153.     {"-n",    STRING_NONZERO,        UNARY_OP},
  154.     {"=",    STRING_EQUAL,        BINARY_OP},
  155.     {"!=",    STRING_NOTEQUAL,    BINARY_OP},
  156.     {"<",    STRING_LESS,        BINARY_OP},
  157.     {">",    STRING_GREATER,        BINARY_OP},
  158.     {"-eq",    NUMBER_EQUAL,        BINARY_OP},
  159.     {"-ne",    NUMBER_NOTEQUAL,    BINARY_OP},
  160.     {"-ge",    NUMBER_EQ_GREAT,    BINARY_OP},
  161.     {"-gt",    NUMBER_GREATER,        BINARY_OP},
  162.     {"-le",    NUMBER_EQ_LESS,        BINARY_OP},
  163.     {"-lt",    NUMBER_LESS,        BINARY_OP},
  164.     {"!",    UNARY_NOT,        B_UNARY_OP},
  165.     {"(",    LPAREN,            PAREN},
  166.     {")",    RPAREN,            PAREN},
  167.     {"&&",    BINARY_AND,        B_BINARY_OP},
  168.     {"||",    BINARY_OR,        B_BINARY_OP},
  169.     {"-c",    FILE_CHARACTER,        UNARY_OP},
  170.     {"-b",    FILE_BLOCK,        UNARY_OP},
  171.     {"-u",    FILE_USER,        UNARY_OP},
  172.     {"-g",    FILE_GROUP,        UNARY_OP},
  173.     {"-k",    FILE_TEXT,        UNARY_OP},
  174.     {"-p",    FILE_FIFO,        UNARY_OP},
  175.     {"-h",    FILE_SYMBOLIC,        UNARY_OP},
  176.     {"-L",    FILE_SYMBOLIC,        UNARY_OP},
  177.     {"-O",    FILE_OWNER,        UNARY_OP},
  178.     {"-G",    FILE_GROUPER,        UNARY_OP},
  179.     {"-S",    FILE_SOCKET,        UNARY_OP},
  180.     {"-nt",    FILE_NEWER,        BINARY_OP},
  181.     {"-ot",    FILE_OLDER,        BINARY_OP},
  182.     {"-ef",    FILE_EQUAL,        BINARY_OP},
  183.     {(char *)NULL,    0,        0}
  184. };
  185.  
  186. /*
  187.  * -o values for set
  188.  */
  189.  
  190. static struct SetOptions {
  191.     char        *OptionName;        /* Option name        */
  192.     unsigned char    FlagValue;        /* Option flag        */
  193.     bool        HasOptionValue;        /* Has -x value        */
  194. } SetOptions[] = {
  195.     { "allexport",    'a',    TRUE },
  196.     { "errexit",    'e',    TRUE },
  197.     { "keyword",    'k',    TRUE },
  198.     { "noexec",        'n',    TRUE },
  199.     { "noglob",        'f',    TRUE },
  200.     { "nounset",    'u',    TRUE },
  201.     { "privileged",    'p',    TRUE },
  202.     { "verbose",    'v',    TRUE },
  203.     { "trackall",    'h',    TRUE },
  204.     { "xtrace",        'x',    TRUE },
  205.     { "ignoreeof",    FLAGS_IGNOREEOF,    FALSE },
  206.     { "markdirs",    FLAGS_MARKDIRECTORY,    FALSE },
  207.     { "noclobber",    FLAGS_NOCLOBER,        FALSE },
  208. #ifdef OS2
  209.     { "realpipes",    FLAGS_REALPIPES,    FALSE },
  210. #endif
  211.     { (char *)NULL,    0,    FALSE }
  212. };
  213.  
  214. /*
  215.  * Getopts values
  216.  */
  217.  
  218. static GetoptsIndex    GetOptsIndex = { 1, 1 };
  219.  
  220. /*
  221.  * General Functions
  222.  */
  223.  
  224. static int near        doOutofMemory (char *);
  225. static int near        TestProcessNextExpression (int);
  226. static int near        TestANDExpression (int);
  227. static int near        TestPrimaryExpression (int);
  228. static int near        TestLexicalAnalysis (char *);
  229. static long near    GetNumberForTest (char *);
  230. static void near    TestSyntaxError (void);
  231. static bool near    ReadALine (int, char *, bool, bool, int *);
  232. static bool near    ChangeOptionValue (char *, bool);
  233. static void near    SetClearFlag (int, bool);
  234. static int near        GetSignalNumber (char *);
  235. static int near        BreakContinueProcessing (char *, int);
  236. static int near        SetUpNewParameterVariables (char **, int, int, char *);
  237. static int near        UsageError (char *);
  238. static void near     PrintEntry (VariableList *, bool, unsigned int);
  239. static int         ComparisonForEnvironmentSort (VariableList **,
  240.                               VariableList **);
  241. static int near     UpdateVariableStatus (char **, unsigned int);
  242. static int near        TypesetVariables (char **);
  243. static int near        ListAllVariables (unsigned int, bool);
  244. static int near        HandleFunction (char *);
  245. static int near        TestOptionValue (char *);
  246. static void near    ResetGetOptions (void);
  247. static int near        GetUnitNumber (char *);
  248. static char near    CleanUpBuffer (int, char *, int);
  249. static struct SetOptions * near LookUpOptionValue (char *);
  250. static void        DisplayVariableEntry (void *, VISIT, int);
  251. #ifdef OS2
  252. static bool near    ConvertJobToPid (char *, PID *);
  253. #endif
  254.  
  255. /*
  256.  * Builtin Commands
  257.  */
  258.  
  259. static int    doexport (int, char **);
  260. static int    doreadonly (int, char **);
  261. static int    domsdos (int, char **);
  262. static int    dotypeset (int, char **);
  263. static int    dounalias (int, char **);
  264. static int    doalias (int, char **);
  265. static int    dolabel (int, char **);
  266. static int    dofalse (int, char **);
  267. static int    dochdir (int, char **);
  268. static int    dodrive (int, char **);
  269. static int    doshift (int, char **);
  270. static int    doumask (int, char **);
  271. static int    dodot (int, char **);
  272. static int    doecho (int, char **);
  273. static int    dolet (int, char **);
  274. #ifdef OS2
  275. static int    dodetach (int, char **);
  276. static int    dostart (int, char **);
  277. static int    dokill (int, char **);
  278. static int    dojobs (int, char **);
  279. static int    dowait (int, char **);
  280. #endif
  281. static int    dogetopts (int, char **);
  282. static int    dopwd (int, char **);
  283. static int    doswap (int, char **);
  284. static int    dounset (int, char **);
  285. static int    dowhence (int, char **);
  286. static int    dofc (int, char **);
  287. static int    dotest (int, char **);
  288. static int    dover (int, char **);
  289. static int    doread (int, char **);
  290. static int    doeval (int, char **);
  291. static int    dotrap (int, char **);
  292. static int    dobuiltin (int, char **);
  293. static int    dobreak (int, char **);
  294. static int    docontinue (int, char **);
  295. static int    doexit (int, char **);
  296. static int    doreturn (int, char **);
  297. static int    doset (int, char **);
  298. static int    dofunctions (int, char **);
  299. static int    dohistory (int, char **);
  300.  
  301. /*
  302.  * TWALK global values for DisplayVariable
  303.  */
  304.  
  305. static unsigned int    DVE_Mask;
  306. static bool         DVE_PrintValue;
  307.  
  308. /*
  309.  * Local data structure for test command
  310.  */
  311.  
  312. static char            **TestArgumentList;
  313. static struct TestOperator    *CurrentTestOperator;
  314. static jmp_buf            TestErrorReturn;
  315. static char            *TestProgram;
  316. static bool            NewTestProgram;
  317.  
  318. /*
  319.  * Static structure for typeset
  320.  */
  321.  
  322. static struct TypesetValues {
  323.     unsigned int    Flags_On;
  324.     unsigned int    Flags_Off;
  325.     int            Base;
  326.     int            Width;
  327. } TypesetValues;
  328.  
  329. /*
  330.  * Current position in Getoption string
  331.  */
  332.  
  333. static int    GetOptionPosition = 1;        /* Current position    */
  334. static int    BadOptionValue = 0;        /* The bad option value    */
  335.  
  336. /*
  337.  * Common strings
  338.  */
  339.  
  340. static char    *DoubleHypen = "--";
  341. static char    *DoublePlus = "++";
  342. static char    *TypeSetUsage = "typeset [ [ [+|-][Hflprtux] ] [+|-][LRZi[n]] [ name [=value] ...]";
  343. static char    *NotBuiltinCommand = "not a builtin\n";
  344. static char    *NotAnAlias = "%s: %s is not an alias\n";
  345. static char    *NotValidAlias = "Invalid alias name";
  346. static char    *Reply_Array[] = {LIT_REPLY, (char *)NULL};
  347. static char    *BadDrive = "%c: bad drive\n";
  348. static char    *ShellInternalCommand = "is a shell internal command";
  349. static char    *FCTooLong = "fc: command line too long\n";
  350. static char    LIT_alias[] = "alias";
  351. static char    LIT_print[] = "print";
  352. static char    LIT_read[] = "read";
  353. static char    LIT_shift[] = "shift";
  354. static char    LIT_break[] = "break";
  355. static char    LIT_builtin[] = "builtin";
  356. static char    *BuiltinUsage = "builtin [ -ads ] [ commands ]";
  357. static char    LIT_continue[] = "continue";
  358. static char    LIT_type[] = "type";
  359. static char    LIT_unalias[] = "unalias";
  360. static char    LIT_unfunction[] = "unfunction";
  361. static char    *LIT_bun = "bad unit number";
  362. #ifdef OS2
  363. static char    *JobUsage = "jobs [-lp] [ -P pid]";
  364. static char    *KillUsage = "kill [ [-l] | [ [-sig] [ process id | %job number ] ... ] ]";
  365. #endif
  366.  
  367. /*
  368.  * Disable variables mapping
  369.  */
  370.  
  371. struct    DisableVariableMap {
  372.     char    *name;
  373.     int        flag;
  374. } DisableVariableMap [] = {
  375.     { MailCheckVariable,    DISABLE_MAILCHECK },
  376.     { OptArgVariable,        DISABLE_OPTARG },
  377.     { OptIndVariable,        DISABLE_OPTIND },
  378.     { SecondsVariable,        DISABLE_SECONDS },
  379.     { RandomVariable,        DISABLE_RANDOM },
  380.     { LastWordVariable,        DISABLE_LASTWORD },
  381.     { LineCountVariable,    DISABLE_LINECOUNT },
  382.     { (char *)NULL,        0 },
  383. };
  384.  
  385. /*
  386.  * built-in commands: : and true
  387.  */
  388.  
  389. static int dolabel (int argc, char **argv)
  390. {
  391.     return 0;
  392. }
  393.  
  394. static int dofalse (int argc, char **argv)
  395. {
  396.     return 1;
  397. }
  398.  
  399. /*
  400.  * Getopts - split arguments.  getopts optstring name [ arg ... ]
  401.  */
  402.  
  403. static int dogetopts (int argc, char **argv)
  404. {
  405.     char    **Arguments;
  406.     char    *OptionString;
  407.     int        result;
  408.     char    SResult[3];
  409.     char    BadResult[2];
  410.     int        Mode = GETOPT_MESSAGE | GETOPT_PLUS;
  411.  
  412.     if (argc < 3)
  413.     return UsageError ("getopts optstring name [ arg ... ]");
  414.  
  415.     memset (SResult, 0, 3);
  416.  
  417. /*
  418.  * A leading : in optstring causes getopts to store the letter of an
  419.  * invalid option in OPTARG, and to set name to ? for an unknown option and
  420.  * to : when a required option is missing.
  421.  */
  422.  
  423.     if (*(OptionString = argv[1]) == ':')
  424.     {
  425.     OptionString++;
  426.     Mode = GETOPT_PLUS;
  427.     }
  428.  
  429. /*
  430.  * Use positional parameters
  431.  */
  432.  
  433.     if (argc == 3)
  434.     {
  435.     argc = ParameterCount + 1;
  436.     Arguments = ParameterArray;
  437.     }
  438.  
  439. /* No - use supplied */
  440.  
  441.     else
  442.     {
  443.     Arguments = &argv[2];        /* Adjust pointers        */
  444.     argc -= 2;
  445.     }
  446.  
  447. /*
  448.  * Get the value of OPTIND and initialise the getopt function
  449.  */
  450.  
  451.     if (!(DisabledVariables & DISABLE_OPTIND))
  452.     OptionIndex = (int)GetVariableAsNumeric (OptIndVariable);
  453.  
  454.     else
  455.     OptionIndex = GetOptsIndex.Index;
  456.  
  457. /* Initialise the other values */
  458.  
  459.     GetOptionPosition = GetOptsIndex.SubIndex;
  460.     OptionArgument = (char *)NULL;
  461.  
  462.     result = GetOptions (argc, Arguments, OptionString, Mode);
  463.  
  464. /* Save new positions */
  465.  
  466.     SaveGetoptsValues (OptionIndex, GetOptionPosition);
  467.  
  468. /* Check for EOF */
  469.  
  470.     if (result == EOF)
  471.     return 1;
  472.  
  473. /* Set up result string */
  474.  
  475.     *SResult = (char)result;
  476.  
  477. /* Did we get an error.  Yes.  If message output, don't put value
  478.  * in OPTARG
  479.  */
  480.  
  481.     if (result == '?')
  482.     {
  483.     if (Mode & GETOPT_MESSAGE)
  484.         OptionArgument = (char *)NULL;
  485.  
  486. /* Error, set up values in optarg and the result */
  487.  
  488.     else
  489.     {
  490.         SResult[0] = (char)((OptionArgument == (char *)NULL) ? '?' : ':');
  491.         *(OptionArgument = BadResult) = (char)BadOptionValue;
  492.         *(OptionArgument + 1) = 0;
  493.     }
  494.     }
  495.  
  496. /* If the argument started with a +, tell them */
  497.  
  498.     else if (OptionStart == '+')
  499.     {
  500.       SResult[1] = (char)result;
  501.       SResult[0] = '+';
  502.     }
  503.  
  504. /* If we got an argument, set the various shell variables */
  505.  
  506.     if ((OptionArgument != (char *)NULL) &&
  507.     (!(DisabledVariables & DISABLE_OPTARG)))
  508.     SetVariableFromString (OptArgVariable, OptionArgument);
  509.  
  510.     SetVariableFromString (argv[2], SResult);
  511.     return 0;
  512. }
  513.  
  514. /*
  515.  * Reset the getopts values
  516.  */
  517.  
  518. void    ResetGetoptsValues (bool Variable)
  519. {
  520.     if (Variable && (!(DisabledVariables & DISABLE_OPTIND)))
  521.     SetVariableFromNumeric (OptIndVariable, 1L);
  522.  
  523.     GetOptsIndex.Index = 1;
  524.     GetOptsIndex.SubIndex = 1;
  525. }
  526.  
  527. /*
  528.  * Save the new Getopts values
  529.  */
  530.  
  531. void    SaveGetoptsValues (int Index, int Position)
  532. {
  533.     if (!(DisabledVariables & DISABLE_OPTIND))
  534.     SetVariableFromNumeric (OptIndVariable, (long)Index);
  535.  
  536.     GetOptsIndex.Index = Index;
  537.     GetOptsIndex.SubIndex = Position;
  538. }
  539.  
  540. /*
  541.  * Get the current Getopts values
  542.  */
  543.  
  544. void    GetGetoptsValues (GetoptsIndex *values)
  545. {
  546.     values->Index = GetOptsIndex.Index;
  547.     values->SubIndex = GetOptsIndex.SubIndex;
  548. }
  549.  
  550. /*
  551.  * Echo the parameters:  echo [ -n ] parameters
  552.  */
  553.  
  554. static int doecho (int argc, char **argv)
  555. {
  556.     int        flags = ECHO_ESCAPE;
  557.     FILE    *fp = stdout;
  558.     char    *ip;            /* Input pointer        */
  559.     char    *op;
  560.     int        c, c1;
  561.     int        R_flag = GETOPT_PRINT;    /* Enable -n test        */
  562.  
  563.     ResetGetOptions ();            /* Reset GetOptions        */
  564.  
  565. /* Echo or print? */
  566.  
  567.     if (strcmp (*argv, LIT_print) == 0)
  568.     {
  569.     R_flag = 0;            /* Reset        */
  570.  
  571.     while ((c = GetOptions (argc, argv, "Rnprsu:", R_flag)) != EOF)
  572.     {
  573.         switch (c)
  574.         {
  575.         case 'R':
  576.             R_flag = GETOPT_PRINT;
  577.             flags &= ~ECHO_ESCAPE;
  578.             break;
  579.  
  580.         case 'n':
  581.             flags = ECHO_NO_EOL;
  582.             break;
  583.  
  584.         case 'r':
  585.             flags &= ~ECHO_ESCAPE;
  586.             break;
  587.  
  588.         case 's':
  589.             flags |= ECHO_HISTORY;
  590.             break;
  591.  
  592.         case 'p':
  593.             break;
  594.  
  595.         case 'u':
  596.             if ((c = GetUnitNumber (LIT_print)) == -1)
  597.             return 1;
  598.  
  599.             if ((fp = fdopen (c, "wt")) == (FILE *)NULL)
  600.             return PrintWarningMessage (LIT_Emsg, LIT_bun,
  601.                             LIT_print, OptionArgument);
  602.  
  603.             break;
  604.  
  605.         default:
  606.             return UsageError ("print [ -Rpnrsu[unit] ] ...");
  607.         }
  608.     }
  609.     }
  610.  
  611.     if ((OptionIndex < argc) && (R_flag == GETOPT_PRINT) &&
  612.     (!strcmp (argv[OptionIndex], "-n")))
  613.     {
  614.     flags |= ECHO_NO_EOL;
  615.     ++OptionIndex;
  616.     }
  617.  
  618.     argv += OptionIndex;
  619.  
  620. /* Clear the history buffer so we can use it */
  621.  
  622.     FlushHistoryBuffer ();
  623.     op = ConsoleLineBuffer;
  624.  
  625. /* Process the arguments.  Process \ as a special if necessary */
  626.  
  627.     while ((ip = *(argv++)) != (char *)NULL)
  628.     {
  629.  
  630. /* If echo too big - disable history save */
  631.  
  632.     if ((op - ConsoleLineBuffer) > LINE_MAX - 4)
  633.     {
  634.         *op = 0;
  635.         fputs (op = ConsoleLineBuffer, fp);
  636.  
  637.         if (flags & ECHO_HISTORY)
  638.         fprintf (stderr, BasicErrorMessage, "Line too long for history",
  639.              LIT_print);
  640.  
  641.         flags &= ~ECHO_HISTORY;
  642.     }
  643.  
  644. /* Process the character */
  645.  
  646.     while (c = *(ip++))
  647.     {
  648.         if ((flags & ECHO_ESCAPE) && (c == '\\'))
  649.         {
  650.         c1 = *ip;
  651.  
  652.         if ((c = ProcessOutputMetaCharacters (&ip)) == -1)
  653.         {
  654.             flags |= ECHO_NO_EOL;
  655.             continue;
  656.         }
  657.  
  658. /* If unchanged - output backslash */
  659.  
  660.         else if ((c == c1) && (c != '\\'))
  661.             *(op++) = '\\';
  662.         }
  663.  
  664.         *(op++) = (char)c;
  665.     }
  666.  
  667. /* End of string - check to see if a space if required */
  668.  
  669.     if (*argv != (char *)NULL)
  670.         *(op++) = CHAR_SPACE;
  671.     }
  672.  
  673. /* Is EOL required ? */
  674.  
  675.     if (!(flags & ECHO_NO_EOL))
  676.     *(op++) = CHAR_NEW_LINE;
  677.  
  678.     *op = 0;
  679.     fputs (ConsoleLineBuffer, fp);
  680.     fflush (fp);
  681.  
  682.     if (fp != stdout)
  683.     {
  684.     fileno (fp) = -1;
  685.     fclose (fp);
  686.     }
  687.  
  688. /* Save history */
  689.  
  690.     if (flags & ECHO_HISTORY)
  691.     {
  692.     CleanUpBuffer (op - ConsoleLineBuffer, ConsoleLineBuffer, 0x1a);
  693.     AddHistory (FALSE);
  694.     }
  695.  
  696.     return 0;
  697. }
  698.  
  699. /*
  700.  * ProcessOutputMetaCharacters - Convert an escaped character to a binary value.
  701.  *
  702.  * Returns the binary value and updates the string pointer.
  703.  */
  704.  
  705. static struct {
  706.     char    Escaped;
  707.     int        NewValue;
  708. } ConvertMetaCharacters [] =
  709. {
  710.     { 'b',  0x08}, { 'f',  0x0c}, { 'v',  0x0b},  { 'n',  0x0a},
  711.     { 'r',  0x0d}, { 't',  0x09}, { '\\',  '\\'}, { 'c',  -1},
  712.     { 0, 0}
  713. };
  714.  
  715. int ProcessOutputMetaCharacters (char **cp)
  716. {
  717.     int        c_val = **cp;            /* Current character    */
  718.     int        j = 0;
  719.  
  720.     if (c_val)
  721.         (*cp)++;
  722.  
  723. /* Process escaped characters */
  724.  
  725.     while (ConvertMetaCharacters[j].Escaped != 0)
  726.     {
  727.      if (ConvertMetaCharacters[j].Escaped == (char)c_val)
  728.         return ConvertMetaCharacters[j].NewValue;
  729.  
  730.     ++j;
  731.     }
  732.  
  733. /* Check for an octal string */
  734.  
  735.     if (IS_OCTAL (c_val))
  736.     {
  737.     c_val -= '0';
  738.  
  739.     while ((IS_OCTAL (**cp)))
  740.         c_val = (c_val * 8) + *((*cp)++) - '0';
  741.  
  742.     return c_val;
  743.     }
  744.  
  745.     return c_val;
  746. }
  747.  
  748. /*
  749.  * Display the current version: ver
  750.  */
  751.  
  752. static int dover (int argc, char **argv)
  753. {
  754.     PrintVersionNumber (stdout);
  755.     return 0;
  756. }
  757.  
  758. #ifndef OS2
  759. static char    *swap_device[] = {"disk", "extend", "expand"};
  760. #endif
  761.  
  762. /*
  763.  * Modify swapping information: swap [ options ]
  764.  */
  765.  
  766. static int doswap (int argc, char **argv)
  767. {
  768. #ifdef OS2
  769.     puts ("Swapping not available on OS/2");
  770. #else
  771.     register int    n = 1;
  772.     char        *cp;
  773.  
  774. /* Display current values ? */
  775.  
  776.     if (argv[1] == (char *)NULL)
  777.     {
  778.     if (Swap_Mode == SWAP_OFF)
  779.         puts ("Swapping disabled");
  780.  
  781.     else
  782.     {
  783.         register int    j;
  784.  
  785.         fputs ("Swap devices: ", stdout);
  786.  
  787.         for (j = 0, n = 1; j < 3; ++j, n <<= 1)
  788.         {
  789.         if (Swap_Mode & n)
  790.         {
  791.             printf ("%s ", swap_device[j]);
  792.  
  793.             if (n == SWAP_EXTEND)
  794.             printf ("(0x%.6lx) ", SW_EMstart);
  795.         }
  796.         }
  797.  
  798.         putchar (CHAR_NEW_LINE);
  799.     }
  800.  
  801.     return 0;
  802.     }
  803.  
  804. /* Set up new values */
  805.  
  806.     Swap_Mode = SWAP_OFF;
  807.     ClearSwapFile ();
  808.  
  809.     while ((cp = argv[n++]) != (char *)NULL)
  810.     {
  811.     if (strcmp (cp, "off") == 0)
  812.         Swap_Mode = SWAP_OFF;
  813.  
  814.     else if (strcmp (cp, "on") == 0)
  815.         Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
  816.  
  817. /* Scan for valid arguments */
  818.  
  819.     else
  820.     {
  821.         register int    j, k;
  822.  
  823.         for (j = 0, k = 1; j < 3; ++j, k <<= 1)
  824.         {
  825.         if (strcmp (cp, swap_device[j]) == 0)
  826.         {
  827.             Swap_Mode |= k;
  828.  
  829. /* If extended memory, they can specify the start address as a hex number */
  830.  
  831.             if (k == SWAP_EXTEND)
  832.             {
  833.             char    *sp;
  834.             long    start;
  835.  
  836. /* Check for not changed */
  837.  
  838.             if ((sp = argv[n]) == (char *)NULL)
  839.                 break;
  840.  
  841. /* Convert hex number */
  842.  
  843.             if (!ConvertNumericValue (sp, &start, 16))
  844.                 break;
  845.  
  846. /* Set used and saved new value */
  847.  
  848.             SW_EMstart = start;
  849.             ++n;
  850.  
  851.             if ((SW_EMstart < 0x100000L) ||
  852.                 (SW_EMstart > 0xf00000L))
  853.                 SW_EMstart = 0x100000L;
  854.  
  855.             printf ("Extend memory start set to 0x%.6lx\n",
  856.                   SW_EMstart);
  857.             }
  858.  
  859.             break;
  860.         }
  861.         }
  862.  
  863.         if (j == 3)
  864.         return UsageError ("swap [ on | off | disk | expand | extend [ address ] ] ...");
  865.     }
  866.     }
  867.  
  868. #endif
  869.     return 0;
  870. }
  871.  
  872. /*
  873.  * Output the current path: pwd [drives]
  874.  */
  875.  
  876. static int dopwd (int argc, char **argv)
  877. {
  878.     int            i = 1;
  879.     int            RetVal = 0;
  880.     char        *sp;
  881.     unsigned int    ndrive;
  882.     char        ldir[PATH_MAX + 6];
  883.  
  884. /* Print the current directories on the selected drive */
  885.  
  886.     while ((sp = argv[i++]) != (char *)NULL)
  887.     {
  888.  
  889. /* Select the drive and get the path */
  890.  
  891.     while (ndrive = (unsigned int)(*(sp++)))
  892.     {
  893.         errno = 0;
  894.         _getdcwd (tolower (ndrive) - 'a' + 1, ldir, PATH_MAX + 4);
  895.         ldir[PATH_MAX + 5] = 0;
  896.  
  897.         if (errno)
  898.         RetVal = PrintWarningMessage (BadDrive, ndrive);
  899.  
  900.         else
  901.         {
  902. #ifdef OS2
  903.         if (!IsHPFSFileSystem (ldir))
  904. #endif
  905.             strlwr (ldir);
  906.  
  907.         puts (PATH_TO_UNIX (ldir));
  908.         }
  909.     }
  910.     }
  911.  
  912. /* Print the current directory */
  913.  
  914.     if (argv[1] == (char *)NULL)
  915.     puts (CurrentDirectory->value);
  916.  
  917.     return RetVal;
  918. }
  919.  
  920. /*
  921.  * Unset a variable: unset [ flag ] [ variable name... ]
  922.  * Delete a function: unfunction <names ...>
  923.  */
  924.  
  925. static int dounset (int argc, char **argv)
  926. {
  927.     register int    n = 1;
  928.     register bool    Fnc = FALSE;
  929.     FunctionList     *fp;
  930.     char        *cp;
  931.     int            i;
  932.  
  933. /* -f, functions */
  934.  
  935.     if (strcmp (*argv, LIT_unfunction) == 0)
  936.     Fnc = TRUE;
  937.  
  938.     else if ((argc > 1) && (strcmp (argv[1], "-f") == 0))
  939.     {
  940.     n++;
  941.     Fnc = TRUE;
  942.     }
  943.  
  944. /* Unset the variables, flags or functions */
  945.  
  946.     while ((cp = argv[n++]) != (char *)NULL)
  947.     {
  948.     if (!Fnc)
  949.     {
  950.         UnSetVariable (cp, FALSE);
  951.  
  952.         for (i = 0; DisableVariableMap[i].name != (char *)NULL; i++)
  953.         {
  954.         if (strcmp (DisableVariableMap[i].name, cp) == 0)
  955.         {
  956.             DisabledVariables |= DisableVariableMap[i].flag;
  957.             break;
  958.         }
  959.         }
  960.     }
  961.  
  962.     else if ((fp = LookUpFunction (cp)) != (FunctionList *)NULL)
  963.         DeleteFunction (fp->tree);
  964.     }
  965.  
  966.     return 0;
  967. }
  968.  
  969. /* Delete a variable.  If all is set, system variables can be deleted.
  970.  * This is used to delete the trap functions
  971.  */
  972.  
  973. void UnSetVariable (register char *cp, bool all)
  974. {
  975.     VariableList    **vpp;
  976.     VariableList    *vp;
  977.     void        (*save_signal)(int);
  978.  
  979. /* Unset a flag */
  980.  
  981.     if (*cp == '-')
  982.     {
  983.     while (*(++cp) != 0)
  984.     {
  985.         if (islower (*cp))
  986.         FL_CLEAR (*cp);
  987.     }
  988.  
  989.     SetShellSwitches ();
  990.     return;
  991.     }
  992.  
  993. /* Ok - unset a variable and not a local value */
  994.  
  995.     if (!all && !(isalpha (*cp)))
  996.     return;
  997.  
  998. /* Check in list */
  999.  
  1000.     vpp = (VariableList **)tfind (cp, &VariableTree, FindVariable);
  1001.  
  1002. /* If not found, Ignore unset request */
  1003.  
  1004.     if (vpp == (VariableList **)NULL)
  1005.     return;
  1006.  
  1007. /* Error if read-only */
  1008.  
  1009.     vp = *vpp;
  1010.  
  1011.     if (vp->status & (STATUS_READONLY | STATUS_CANNOT_UNSET))
  1012.     {
  1013.     PrintWarningMessage ("unset: %s %s\n", vp->name,
  1014.                  (vp->status & STATUS_CANNOT_UNSET)
  1015.                     ? "cannot be unset" : "is read-only");
  1016.     return;
  1017.     }
  1018.  
  1019. /* Disable signals */
  1020.  
  1021.     save_signal = signal (SIGINT, SIG_IGN);
  1022.  
  1023. /* Delete it */
  1024.  
  1025.     tdelete (cp, &VariableTree, FindVariable);
  1026.     ReleaseMemoryCell ((void *)vp->name);
  1027.  
  1028.     if (vp->value != null)
  1029.     ReleaseMemoryCell ((void *)vp->value);
  1030.  
  1031.     ReleaseMemoryCell ((void *)vp);
  1032.  
  1033. /* Restore signals */
  1034.  
  1035.     signal (SIGINT, save_signal);
  1036. }
  1037.  
  1038. /*
  1039.  * Execute a test: test <arguments>
  1040.  */
  1041.  
  1042. static int dotest (int argc, char **argv)
  1043. {
  1044.     int        st = 0;
  1045.     char    *End;
  1046.  
  1047.     NewTestProgram = (bool)(strcmp (TestProgram = *argv, "[[") == 0);
  1048.  
  1049. /* Note that -a and -o change meaning if [[ ... ]] is used */
  1050.  
  1051.     if (NewTestProgram)
  1052.     {
  1053.     End = "]]";
  1054.     ListOfTestOperators[0].OperatorID = FILE_EXISTS;
  1055.     ListOfTestOperators[0].OperatorType = UNARY_OP;
  1056.     ListOfTestOperators[1].OperatorID = TEST_OPTION;
  1057.     ListOfTestOperators[1].OperatorType = UNARY_OP;
  1058.     }
  1059.  
  1060.     else
  1061.     {
  1062.     End = "]";
  1063.     ListOfTestOperators[0].OperatorID = BINARY_AND;
  1064.     ListOfTestOperators[0].OperatorType = B_BINARY_OP;
  1065.     ListOfTestOperators[1].OperatorID = BINARY_OR;
  1066.     ListOfTestOperators[1].OperatorType = B_BINARY_OP;
  1067.     }
  1068.  
  1069. /* Check out */
  1070.  
  1071.     CurrentTestOperator = (struct TestOperator *)NULL;
  1072.  
  1073. /* If [ <arguments> ] or [[ <arguments> ]] form, check for end ] or ]] and
  1074.  * remove it
  1075.  */
  1076.  
  1077.     if (NewTestProgram || (strcmp (*argv, "[") == 0))
  1078.     {
  1079.     while (argv[++st] != (char *)NULL)
  1080.         ;
  1081.  
  1082.     if (strcmp (argv[--st], End) != 0)
  1083.         return PrintWarningMessage ("%s: missing '%s'\n", TestProgram, End);
  1084.  
  1085.     else
  1086.         argv[st] = (char *)NULL;
  1087.     }
  1088.  
  1089. /* Check for null expression */
  1090.  
  1091.     if (*(TestArgumentList = &argv[1]) == (char *)NULL)
  1092.     return 1;
  1093.  
  1094. /* Set abort address */
  1095.  
  1096.     if (setjmp (TestErrorReturn))
  1097.     return 1;
  1098.  
  1099.     st = !TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
  1100.  
  1101.     if (*(TestArgumentList + 1) != (char *)NULL)
  1102.     TestSyntaxError ();
  1103.  
  1104.     return (st);
  1105. }
  1106.  
  1107. /*
  1108.  * Process next test expression
  1109.  */
  1110.  
  1111. static int near TestProcessNextExpression (int n)
  1112. {
  1113.     int        res;
  1114.  
  1115.     if (n == END_OF_INPUT)
  1116.     TestSyntaxError ();
  1117.  
  1118.     res = TestANDExpression (n);
  1119.  
  1120.     TestArgumentList++;
  1121.  
  1122.     if (TestLexicalAnalysis (*TestArgumentList) == BINARY_OR)
  1123.     {
  1124.     TestArgumentList++;
  1125.  
  1126.     return TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList)) || res;
  1127.     }
  1128.  
  1129.     TestArgumentList--;
  1130.     return res;
  1131. }
  1132.  
  1133. /*
  1134.  * Binary expression ( a AND b)
  1135.  */
  1136.  
  1137. static int near TestANDExpression (int n)
  1138. {
  1139.     int res;
  1140.  
  1141.     if (n == END_OF_INPUT)
  1142.     TestSyntaxError ();
  1143.  
  1144.     res = TestPrimaryExpression (n);
  1145.     TestArgumentList++;
  1146.  
  1147.     if (TestLexicalAnalysis (*TestArgumentList) == BINARY_AND)
  1148.     {
  1149.     TestArgumentList++;
  1150.     return TestANDExpression (TestLexicalAnalysis (*TestArgumentList)) && res;
  1151.     }
  1152.  
  1153.     TestArgumentList--;
  1154.     return res;
  1155. }
  1156.  
  1157. /*
  1158.  * Handle Primary expression
  1159.  */
  1160.  
  1161. static int near TestPrimaryExpression (int n)
  1162. {
  1163.     register char    *opnd1, *opnd2;
  1164.     struct stat        s, s1;
  1165.     int            res;
  1166.  
  1167.     if (n == END_OF_INPUT)
  1168.     TestSyntaxError ();
  1169.  
  1170.     if (n == UNARY_NOT)
  1171.     {
  1172.     TestArgumentList++;
  1173.     return !TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
  1174.     }
  1175.  
  1176.     if (n == LPAREN)
  1177.     {
  1178.     TestArgumentList++;
  1179.     res = TestProcessNextExpression (TestLexicalAnalysis (*TestArgumentList));
  1180.  
  1181.     TestArgumentList++;
  1182.     if (TestLexicalAnalysis (*TestArgumentList) != RPAREN)
  1183.         TestSyntaxError ();
  1184.  
  1185.     return res;
  1186.     }
  1187.  
  1188. /* Operand */
  1189.  
  1190.     if (n == OPERAND)
  1191.     {
  1192.     opnd1 = *(TestArgumentList++);
  1193.     (void) TestLexicalAnalysis (*TestArgumentList);
  1194.  
  1195.     if ((CurrentTestOperator != (struct TestOperator *)NULL) &&
  1196.         (CurrentTestOperator->OperatorType == BINARY_OP))
  1197.     {
  1198.         struct TestOperator    *op = CurrentTestOperator;
  1199.  
  1200.         TestArgumentList++;
  1201.         if ((opnd2 = *TestArgumentList) == (char *)NULL)
  1202.         TestSyntaxError ();
  1203.  
  1204.         switch (op->OperatorID)
  1205.         {
  1206.  
  1207. /* String lengths */
  1208.  
  1209.         case STRING_EQUAL:
  1210.             return strcmp (opnd1, opnd2) == 0;
  1211.  
  1212.         case STRING_NOTEQUAL:
  1213.             return strcmp (opnd1, opnd2) != 0;
  1214.  
  1215.         case STRING_LESS:
  1216.             return strcmp (opnd1, opnd2) < 0;
  1217.  
  1218.         case STRING_GREATER:
  1219.             return strcmp (opnd1, opnd2) > 0;
  1220.  
  1221. /* Numeric comparisions */
  1222.  
  1223.         case NUMBER_EQUAL:
  1224.             return GetNumberForTest (opnd1) == GetNumberForTest (opnd2);
  1225.  
  1226.         case NUMBER_NOTEQUAL:
  1227.             return GetNumberForTest (opnd1) != GetNumberForTest (opnd2);
  1228.  
  1229.         case NUMBER_EQ_GREAT:
  1230.             return GetNumberForTest (opnd1) >= GetNumberForTest (opnd2);
  1231.  
  1232.         case NUMBER_GREATER:
  1233.             return GetNumberForTest (opnd1) > GetNumberForTest (opnd2);
  1234.  
  1235.         case NUMBER_EQ_LESS:
  1236.             return GetNumberForTest (opnd1) <= GetNumberForTest (opnd2);
  1237.  
  1238.         case NUMBER_LESS:
  1239.             return GetNumberForTest (opnd1) < GetNumberForTest (opnd2);
  1240.  
  1241. /* Older and Newer - if file not found - set to current time */
  1242.  
  1243.         case FILE_NEWER:
  1244.         case FILE_OLDER:
  1245.             if (stat (CheckDOSFileName (opnd1), &s) == -1)
  1246.             return 0;
  1247.  
  1248.             if (stat (CheckDOSFileName (opnd2), &s1) == -1)
  1249.             s1.st_mtime = time ((time_t *)NULL);
  1250.  
  1251.             return (op->OperatorID == FILE_NEWER)
  1252.                 ? (s.st_mtime > s1.st_mtime)
  1253.                 : (s.st_mtime < s1.st_mtime);
  1254.  
  1255. /* Equals - difficult on DOS.  So just do want UNIX says */
  1256.  
  1257.         case FILE_EQUAL:
  1258.             if ((stat (CheckDOSFileName (opnd1), &s) == -1) ||
  1259.             (stat (CheckDOSFileName (opnd2), &s1) == -1))
  1260.             return 0;
  1261.  
  1262.             return ((s.st_dev == s1.st_dev) && (s.st_ino < s1.st_ino));
  1263.         }
  1264.     }
  1265.  
  1266.     TestArgumentList--;
  1267.     return strlen (opnd1) > 0;
  1268.     }
  1269.  
  1270. /* unary expression */
  1271.  
  1272.     if ((CurrentTestOperator->OperatorType != UNARY_OP) ||
  1273.     (*++TestArgumentList == 0))
  1274.     TestSyntaxError ();
  1275.  
  1276.     switch (n)
  1277.     {
  1278.     case STRING_ZERO:
  1279.         return strlen (*TestArgumentList) == 0;
  1280.  
  1281.     case STRING_NONZERO:
  1282.         return strlen (*TestArgumentList) != 0;
  1283.  
  1284.     case TEST_OPTION:
  1285.         return TestOptionValue (*TestArgumentList) != 0;
  1286.  
  1287. /* File functions */
  1288.  
  1289.     case FILE_EXISTS:
  1290.         return access (CheckDOSFileName (*TestArgumentList), F_OK) == 0;
  1291.  
  1292.     case FILE_READABLE:
  1293.         return access (CheckDOSFileName (*TestArgumentList), R_OK) == 0;
  1294.  
  1295.     case FILE_WRITABLE:
  1296.         return access (CheckDOSFileName (*TestArgumentList), W_OK) == 0;
  1297.  
  1298.     case FILE_EXECUTABLE:
  1299.         return access (CheckDOSFileName (*TestArgumentList), X_OK) == 0;
  1300.  
  1301.     case FILE_REGULAR:
  1302.         return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
  1303.            S_ISREG(s.st_mode);
  1304.  
  1305.     case FILE_DIRECTRY:
  1306.         return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
  1307.            S_ISDIR(s.st_mode);
  1308.  
  1309.     case FILE_NONZERO:
  1310.         return stat (CheckDOSFileName (*TestArgumentList), &s) == 0 &&
  1311.            (s.st_size > 0L);
  1312.  
  1313.     case FILE_TERMINAL:
  1314.         return isatty ((int)GetNumberForTest (*TestArgumentList));
  1315.  
  1316. /* The following have no meaning on MSDOS or OS/2.  So we always return
  1317.  * fail for compatability
  1318.  */
  1319.  
  1320.     case FILE_USER:
  1321.     case FILE_GROUP:
  1322.     case FILE_TEXT:
  1323.     case FILE_BLOCK:
  1324.     case FILE_CHARACTER:
  1325.     case FILE_FIFO:
  1326.     case FILE_SYMBOLIC:
  1327.     case FILE_SOCKET:
  1328.         return 0;
  1329.  
  1330. /* Under MSDOS and OS/2, we always own the file.  Not necessarily true on
  1331.  * networked versions.  But there is no common way of finding out
  1332.  */
  1333.  
  1334.     case FILE_OWNER:
  1335.     case FILE_GROUPER:
  1336.         return 1;
  1337.     }
  1338. }
  1339.  
  1340. /* Operator or Operand ? */
  1341.  
  1342. static int near TestLexicalAnalysis (register char *s)
  1343. {
  1344.     register struct TestOperator    *op = ListOfTestOperators;
  1345.  
  1346.     if (s == (char *)NULL)
  1347.     return END_OF_INPUT;
  1348.  
  1349.     while (op->OperatorName)
  1350.     {
  1351.     if (strcmp (s, op->OperatorName) == 0)
  1352.     {
  1353.         CurrentTestOperator = op;
  1354.         return op->OperatorID;
  1355.     }
  1356.  
  1357.     op++;
  1358.     }
  1359.  
  1360.     CurrentTestOperator = (struct TestOperator *)NULL;
  1361.     return OPERAND;
  1362. }
  1363.  
  1364. /*
  1365.  * Get a long numeric value
  1366.  */
  1367.  
  1368. static long near GetNumberForTest (register char *s)
  1369. {
  1370.     long    l;
  1371.  
  1372.     if (!ConvertNumericValue (s, &l, 10))
  1373.     TestSyntaxError ();
  1374.  
  1375.     return l;
  1376. }
  1377.  
  1378. /*
  1379.  * test syntax error - abort
  1380.  */
  1381.  
  1382. static void near TestSyntaxError (void)
  1383. {
  1384.     PrintWarningMessage (BasicErrorMessage, TestProgram, "syntax error");
  1385.     longjmp (TestErrorReturn, 1);
  1386. }
  1387.  
  1388. /*
  1389.  * Select a new drive: x:
  1390.  *
  1391.  * Select the drive, get the current directory and check that we have
  1392.  * actually selected the drive
  1393.  */
  1394.  
  1395. static int dodrive (int argc, char **argv)
  1396. {
  1397.     unsigned int    ndrive = tolower (**argv) - 'a' + 1;
  1398.  
  1399.     if (argc != 1)
  1400.     return UsageError ("drive:");
  1401.  
  1402.     SetCurrentDrive (ndrive);
  1403.     GetCurrentDirectory ();
  1404.  
  1405.     if (ndrive != GetCurrentDrive ())
  1406.     return PrintWarningMessage (BadDrive, **argv);
  1407.  
  1408.     else
  1409.     return 0;
  1410. }
  1411.  
  1412. /*
  1413.  * Select a new directory:
  1414.  *
  1415.  * cd [ directory ]            Select new directory
  1416.  * cd -                 Select previous directory
  1417.  * cd <search string> <new string>    Select directory based on current
  1418.  * cd                    Select Home directory
  1419.  */
  1420.  
  1421. static int dochdir (int argc, char **argv)
  1422. {
  1423.     char        *NewDirectory;    /* Original new directory    */
  1424.     char        *CNDirectory;    /* New directory        */
  1425.     register char    *cp;        /* In CDPATH Pointer        */
  1426.     char        *directory;
  1427.     int            first = 0;
  1428.     unsigned int    Length;
  1429.     unsigned int    cdrive;
  1430.  
  1431. /* If restricted shell - illegal */
  1432.  
  1433.     if (CheckForRestrictedShell ("cd"))
  1434.     return 1;
  1435.  
  1436.     if (argc > 3)
  1437.     return UsageError ("cd [ directory | - | [ old string ] [ new string]");
  1438.  
  1439. /* Use default ? */
  1440.  
  1441.     if (((NewDirectory = argv[1]) == (char *)NULL) &&
  1442.     ((NewDirectory = GetVariableAsString (HomeVariableName,
  1443.                           FALSE)) == null))
  1444.     return PrintWarningMessage ("cd: no home directory\n");
  1445.  
  1446.     if ((directory = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  1447.     return doOutofMemory ("cd");
  1448.  
  1449.     if ((strcmp (NewDirectory, ShellOptionsVariable) == 0) &&
  1450.     ((NewDirectory = GetVariableAsString (OldPWDVariable, FALSE)) == null))
  1451.     return PrintWarningMessage ("cd: no previous directory\n");
  1452.  
  1453. /* Check for substitue */
  1454.  
  1455.     if ((argv[1] != (char *)NULL) && (argv[2] != (char *)NULL))
  1456.     {
  1457.     if ((cp = strstr (CurrentDirectory->value, argv[1])) == (char *)NULL)
  1458.         return PrintWarningMessage ("cd: string not in pwd: %s", argv[1]);
  1459.  
  1460.     if (strlen (CurrentDirectory->value) - strlen (argv[1]) +
  1461.         strlen (argv[2]) >= FFNAME_MAX)
  1462.         return PrintWarningMessage ("cd: new directory string too long: %s",
  1463.                     argv[1]);
  1464. /* Do the substitution */
  1465.  
  1466.     Length = cp - CurrentDirectory->value;
  1467.     strncpy (NewDirectory, CurrentDirectory->value, Length);
  1468.     strcpy (NewDirectory + Length, argv[2]);
  1469.     strcat (NewDirectory,
  1470.         CurrentDirectory->value + strlen (argv[1]) + Length);
  1471.     }
  1472.  
  1473. /* Save the current drive */
  1474.  
  1475.     cdrive = GetCurrentDrive ();
  1476.  
  1477. /* Remove trailing / */
  1478.  
  1479.     Length = strlen (NewDirectory) - 1;
  1480.  
  1481.     if ((NewDirectory[Length] == CHAR_UNIX_DIRECTORY) &&
  1482.     (!((!Length) || ((Length == 2) && (NewDirectory[1] == ':')))))
  1483.     NewDirectory[Length] = 0;
  1484.  
  1485. /* Scan for the directory.  If there is not a / or : at start, use the
  1486.  * CDPATH variable
  1487.  */
  1488.  
  1489.     cp = ((*NewDirectory == CHAR_UNIX_DIRECTORY) ||
  1490.       (*(NewDirectory + 1) == ':'))
  1491.         ? null : GetVariableAsString ("CDPATH", FALSE);
  1492.  
  1493.     do
  1494.     {
  1495.     cp = BuildNextFullPathName (cp, NewDirectory, directory);
  1496.  
  1497. /* Check for new disk drive */
  1498.  
  1499.     CNDirectory = directory;
  1500.  
  1501.     if (*(CNDirectory + 1) == ':')
  1502.     {
  1503.         SetCurrentDrive (tolower (*CNDirectory) - 'a' + 1);
  1504.         CNDirectory += 2;
  1505.     }
  1506.  
  1507. /* Was the change successful? */
  1508.  
  1509.     if ((!*CNDirectory) || (chdir (CheckDOSFileName (CNDirectory)) == 0))
  1510.     {
  1511.  
  1512. /* OK - reset the current directory (in the shell) and display the new
  1513.  * path if appropriate
  1514.  */
  1515.  
  1516.         GetCurrentDirectory ();
  1517.  
  1518.         if (first)
  1519.         puts (CurrentDirectory->value);
  1520.  
  1521.         return 0;
  1522.     }
  1523.  
  1524.     first = 1;
  1525.  
  1526.     } while (cp != (char *)NULL);
  1527.  
  1528. /* Restore our original drive and restore directory info */
  1529.  
  1530.     SetCurrentDrive (cdrive);
  1531.     GetCurrentDirectory ();
  1532.  
  1533.     return PrintWarningMessage (BasicErrorMessage, NewDirectory,
  1534.                 "bad directory");
  1535. }
  1536.  
  1537. /*
  1538.  * Extract the next path from a string and build a new path from the
  1539.  * extracted path and a file name
  1540.  *
  1541.  * path_s - Path string
  1542.  * file_s - File name string
  1543.  * output_s - Output path
  1544.  */
  1545.  
  1546. char *BuildNextFullPathName (register char *path_s, register char *file_s,
  1547.                  char *output_s)
  1548. {
  1549.     register char    *s = output_s;
  1550.     int            fsize = 0;
  1551.  
  1552.     while (*path_s && (*path_s != ';') && (fsize++ < FFNAME_MAX))
  1553.     *s++ = *path_s++;
  1554.  
  1555.     if ((output_s != s) && (*(s - 1) != CHAR_UNIX_DIRECTORY) &&
  1556.     (fsize++ < FFNAME_MAX))
  1557.     *s++ = CHAR_UNIX_DIRECTORY;
  1558.  
  1559.     *s = '\0';
  1560.  
  1561.     if (file_s != (char *)NULL)
  1562.     strncpy (s, file_s, FFNAME_MAX - fsize);
  1563.  
  1564.     output_s[FFNAME_MAX - 1] = 0;
  1565.  
  1566.     return (*path_s ? ++path_s : (char *)NULL);
  1567. }
  1568.  
  1569. /*
  1570.  * Execute a shift command: shift [ n ]
  1571.  */
  1572.  
  1573. static int doshift (int argc, char **argv)
  1574. {
  1575.     register int    n;
  1576.     char        *Nvalue = argv[1];
  1577.  
  1578.     if (argc > 2)
  1579.     return UsageError ("shift [ count ]");
  1580.  
  1581.     n = (Nvalue != (char *)NULL) ? GetNumericValue (Nvalue) : 1;
  1582.  
  1583.     if (n < 0)
  1584.     return PrintWarningMessage (LIT_Emsg, LIT_shift, "bad shift value",
  1585.                     Nvalue);
  1586.  
  1587.     if (ParameterCount < n)
  1588.     return PrintWarningMessage (BasicErrorMessage, LIT_shift,
  1589.                     "nothing to shift");
  1590.  
  1591.     return SetUpNewParameterVariables (ParameterArray, n + 1,
  1592.                        ParameterCount + 1, LIT_shift);
  1593. }
  1594.  
  1595. /*
  1596.  * Execute a umask command: umask [ n ]
  1597.  */
  1598.  
  1599. static int doumask (int argc, char **argv)
  1600. {
  1601.     register int    i;
  1602.     register char    *cp;
  1603.     long        value;
  1604.  
  1605.     if (argc > 2)
  1606.     return UsageError ("umask [ new mask ]");
  1607.  
  1608.     if ((cp = argv[1]) == (char *)NULL)
  1609.     {
  1610.     umask ((i = umask (0)));
  1611.     printf ("%o\n", i);
  1612.     }
  1613.  
  1614.     else
  1615.     {
  1616.     if (!ConvertNumericValue (cp, &value, 8))
  1617.         return PrintWarningMessage ("umask: bad mask (%s)\n", cp);
  1618.  
  1619.     umask ((int)value);
  1620.     }
  1621.  
  1622.     return 0;
  1623. }
  1624.  
  1625. /*
  1626.  * Execute an exec command: exec [ arguments ]
  1627.  */
  1628.  
  1629. int    doexec (C_Op *t)
  1630. {
  1631.     register int    i;
  1632.     jmp_buf        ex;
  1633.     int            *ofail;
  1634.  
  1635. /* Shift arguments */
  1636.  
  1637.     for (i = 0; (t->words[i] = t->words[i + 1]) != (char *)NULL; i++)
  1638.     ;
  1639.  
  1640. /* Left the I/O as it is */
  1641.  
  1642.     if (i == 0)
  1643.     return RestoreStandardIO (0, FALSE);
  1644.  
  1645.     ProcessingEXECCommand = TRUE;
  1646.     ofail = FailReturnPoint;
  1647.  
  1648. /* Set execute function recursive level to zero */
  1649.  
  1650.     Execute_stack_depth = 0;
  1651.     t->ioact = (IO_Actions **)NULL;
  1652.  
  1653.     if (setjmp (FailReturnPoint = ex) == 0)
  1654.     ExecuteParseTree (t, NOPIPE, NOPIPE, EXEC_WITHOUT_FORK);
  1655.  
  1656. /* Clear the extended file if an interrupt happened */
  1657.  
  1658.     ClearExtendedLineFile ();
  1659.     FailReturnPoint = ofail;
  1660.     ProcessingEXECCommand = FALSE;
  1661.     return 1;
  1662. }
  1663.  
  1664. /*
  1665.  * Execute a script in the current shell: . <filename>
  1666.  */
  1667.  
  1668. static int dodot (int argc, char **argv)
  1669. {
  1670.     register int    i;
  1671.     register char    *sp;
  1672.     char        *cp;
  1673.     char        *l_path;
  1674.  
  1675.     if ((cp = argv[1]) == (char *)NULL)
  1676.     return 0;
  1677.  
  1678. /* Get some space */
  1679.  
  1680.     if ((l_path = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  1681.     return doOutofMemory (".");
  1682.  
  1683. /* Save the current drive */
  1684.  
  1685.     sp = (any (CHAR_UNIX_DIRECTORY, cp) || (*(cp + 1) == ':'))
  1686.         ? null : GetVariableAsString (PathLiteral, FALSE);
  1687.  
  1688.     do
  1689.     {
  1690.     sp = BuildNextFullPathName (sp, cp, l_path);
  1691.  
  1692.     if ((i = OpenForExecution (l_path, (char **)NULL, (int *)NULL)) >= 0)
  1693.         return RUN (afile, ReMapIOHandler (i), File_GetNextCharacter,
  1694.             FALSE, l_path, &argv[1]);
  1695.  
  1696.     } while (sp != (char *)NULL);
  1697.  
  1698.     return PrintWarningMessage (BasicErrorMessage, cp, NotFound);
  1699. }
  1700.  
  1701. /*
  1702.  * Read from standard input into a variable list
  1703.  *
  1704.  * read [-prs] [-u unit] [ variable list ]
  1705.  */
  1706.  
  1707. static int doread (int argc, char **argv)
  1708. {
  1709.     register char    *cp, *op;
  1710.     register int    i;
  1711.     int            Unit = STDIN_FILENO;
  1712.     bool        EndOfInputDetected = FALSE;
  1713.     int            PreviousCharacter = 0;
  1714.     bool        SaveMode = FALSE;
  1715.     bool        RawMode = FALSE;
  1716.     char        *Prompt = (char *)NULL;
  1717.     char        *NewBuffer;
  1718.     int            eofc;
  1719.  
  1720.     if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 1)) == (char *)NULL)
  1721.     return doOutofMemory (LIT_read);
  1722.  
  1723. /* Check for variable name.  If not defined, use $REPLY */
  1724.  
  1725.     ResetGetOptions ();            /* Reset GetOptions        */
  1726.  
  1727.     while ((i = GetOptions (argc, argv, "prsu:", 0)) != EOF)
  1728.     {
  1729.     switch (i)
  1730.     {
  1731.         case 'p':            /* Clean up process        */
  1732.         break;
  1733.  
  1734.         case 'r':            /* Raw Mode            */
  1735.         RawMode = TRUE;
  1736.         break;
  1737.  
  1738.         case 's':            /* Save a command        */
  1739.         SaveMode = TRUE;
  1740.         break;
  1741.  
  1742.         case 'u':            /* Specify input unit        */
  1743.         if ((Unit = GetUnitNumber (LIT_read)) == -1)
  1744.             return 2;
  1745.  
  1746.         default:
  1747.         return UsageError ("read [ -prs ] [ -u unit ] [ name?prompt ] [ name ... ]");
  1748.     }
  1749.     }
  1750.  
  1751.     argv += OptionIndex;
  1752.     argc -= OptionIndex;
  1753.  
  1754.     if (!argc)
  1755.     argv = Reply_Array;
  1756.  
  1757. /* Get the prompt and write it */
  1758.  
  1759.     LastUserPrompt = null;
  1760.  
  1761.     if ((Prompt = strchr (argv[0], '?')) != (char *)NULL)
  1762.     {
  1763.     *(Prompt++) = 0;
  1764.     LastUserPrompt1 = Prompt;
  1765.     LastUserPrompt = (char *)NULL;
  1766.     }
  1767.  
  1768. /* Clear the history buffer */
  1769.  
  1770.     FlushHistoryBuffer ();
  1771.  
  1772. /* Read the line from the device */
  1773.  
  1774.     if (ReadALine (Unit, Prompt, SaveMode, FALSE, &eofc))
  1775.     return 1;
  1776.  
  1777.     cp = ConsoleLineBuffer;
  1778.  
  1779. /* For each variable, read the data until a white space is detected */
  1780.  
  1781.     while (*argv != (char *)NULL)
  1782.     {
  1783.  
  1784. /* Read in until end of line, file or a field separator is detected */
  1785.  
  1786.     op = (char *)NULL;
  1787.  
  1788.     while (!EndOfInputDetected && *cp)
  1789.     {
  1790.  
  1791. /* End of file */
  1792.  
  1793.         if (*cp == (char)eofc)
  1794.         return 1;
  1795.  
  1796. /* Check for Newline or IFS character */
  1797.  
  1798.         if ((*cp == CHAR_NEW_LINE) ||
  1799.         ((argv[1] != (char *)NULL) &&
  1800.          any (*cp, GetVariableAsString (IFS, FALSE))))
  1801.         {
  1802.         if (*cp != CHAR_NEW_LINE)
  1803.              ;
  1804.  
  1805.         else if ((PreviousCharacter != '\\') || (RawMode))
  1806.             EndOfInputDetected = TRUE;
  1807.  
  1808. /* Handle continuation line */
  1809.  
  1810.         else if (ReadALine (Unit, Prompt, SaveMode, TRUE, &eofc))
  1811.             return 1;
  1812.  
  1813.         else
  1814.         {
  1815.             cp = ConsoleLineBuffer;
  1816.             PreviousCharacter = 0;
  1817.             continue;
  1818.         }
  1819.  
  1820.         break;
  1821.         }
  1822.  
  1823. /* Save the current character */
  1824.  
  1825.         if (op == (char *)NULL)
  1826.         op = NewBuffer;
  1827.  
  1828.         *(op++) = *cp;
  1829.         PreviousCharacter = *(cp++);
  1830.     }
  1831.  
  1832. /* Skip over terminating character */
  1833.  
  1834.     if (*cp)
  1835.         ++cp;
  1836.  
  1837. /* Check for null string */
  1838.  
  1839.     if (op == (char *)NULL)
  1840.         op = null;
  1841.  
  1842. /* Terminate the string */
  1843.  
  1844.     else
  1845.     {
  1846.         *op = 0;
  1847.  
  1848.         if (!strlen (NewBuffer))
  1849.         continue;
  1850.  
  1851.         else
  1852.         op = NewBuffer;
  1853.     }
  1854. /* Save the string value */
  1855.  
  1856.     SetVariableFromString (*(argv++), op);
  1857.     }
  1858.  
  1859.     ReleaseMemoryCell ((void *)NewBuffer);
  1860.  
  1861.     return 0;
  1862. }
  1863.  
  1864. /*
  1865.  * Read a line from either the console or a file for the read command.
  1866.  * The line is returned in the ConsoleLineBuffer.
  1867.  */
  1868.  
  1869. static bool near ReadALine (int Unit, char *Prompt, bool SaveMode,
  1870.                 bool Append, int *eofc)
  1871. {
  1872.     int        NumberBytesRead;
  1873.     char    *cp;
  1874.     int        x;
  1875.  
  1876. /* Generate the prompt */
  1877.  
  1878.     if ((Prompt != (char *)NULL) && (!IsConsole (Unit)) &&
  1879.     (!isatty (Unit) || (write (Unit, Prompt, strlen (Prompt)) == -1)))
  1880.     fputs (Prompt, stderr);
  1881.  
  1882. /* Read the line */
  1883.  
  1884.     *eofc = 0x1a;
  1885.  
  1886.     if (IsConsole (Unit))
  1887.     {
  1888.     *eofc = GetEOFKey ();
  1889.     GetLineFromConsole ();            /* No - get input    */
  1890.  
  1891.     if (((x = strlen(ConsoleLineBuffer)) != 1) &&
  1892.         (ConsoleLineBuffer[x] == (char)*eofc))
  1893.         ConsoleLineBuffer[x] = 0;
  1894.  
  1895.     strcat (ConsoleLineBuffer, "\n");
  1896.     }
  1897.  
  1898.     else
  1899.     {
  1900.     NumberBytesRead = 0;
  1901.     cp =  ConsoleLineBuffer;
  1902.  
  1903.     while (NumberBytesRead++ < LINE_MAX)
  1904.     {
  1905.         if ((x = read (Unit, cp, 1)) == -1)
  1906.         return TRUE;
  1907.  
  1908. /* EOF detected as first character on line */
  1909.  
  1910.         if ((NumberBytesRead == 1) && ((!x) || (*cp == (char)*eofc)))
  1911.         return TRUE;
  1912.  
  1913. /* End read if NEWLINE or EOF character detected */
  1914.  
  1915.         if ((!x) || (*cp == CHAR_NEW_LINE) || (*cp == (char)*eofc))
  1916.         break;
  1917.  
  1918.         cp++;
  1919.     }
  1920.  
  1921. /* Terminate the line the same in all cases */
  1922.  
  1923.     *(cp++) = CHAR_NEW_LINE;
  1924.     *cp = 0;
  1925.     }
  1926.  
  1927. /* Save the history.  Clean it up first */
  1928.  
  1929.     if (SaveMode)
  1930.     {
  1931.     char    save;
  1932.  
  1933.     save = CleanUpBuffer (strlen (ConsoleLineBuffer), ConsoleLineBuffer,
  1934.                   *eofc);
  1935.     AddHistory (Append);
  1936.     ConsoleLineBuffer[strlen(ConsoleLineBuffer)] = save;
  1937.     }
  1938.  
  1939.     return FALSE;
  1940. }
  1941.  
  1942. /*
  1943.  * Evaluate an expression: eval <expression>
  1944.  */
  1945.  
  1946. static int doeval (int argc, char **argv)
  1947. {
  1948.     return RUN (awordlist, argv + 1, WordList_GetNextCharacter, TRUE,
  1949.         null, (char **)NULL);
  1950. }
  1951.  
  1952. /*
  1953.  * Execute a trap: trap [ number... ] [ command ]
  1954.  */
  1955.  
  1956. static int dotrap (int argc, char **argv)
  1957. {
  1958.     register int    n, i;
  1959.     register bool    ResetSignal;
  1960.     char        tval[10];
  1961.     char        *cp;
  1962.  
  1963.     if (argc < 2)
  1964.     {
  1965.  
  1966. /* Display trap - look up each trap and print those we find */
  1967.  
  1968.     for (i = 0; i < NSIG; i++)
  1969.     {
  1970.         *tval = '~';
  1971.         itoa (i, tval + 1, 10);
  1972.  
  1973.         if ((cp = GetVariableAsString (tval, FALSE)) != null)
  1974.         printf ("%u: %s\n", i, cp);
  1975.     }
  1976.  
  1977.     if ((cp = GetVariableAsString (Trap_DEBUG, FALSE)) != null)
  1978.         printf (BasicErrorMessage, &Trap_DEBUG[1], cp);
  1979.  
  1980.     if ((cp = GetVariableAsString (Trap_ERR, FALSE)) != null)
  1981.         printf (BasicErrorMessage, &Trap_ERR[1], cp);
  1982.  
  1983.     return 0;
  1984.     }
  1985.  
  1986. /* Check to see if signal re-set */
  1987.  
  1988.     ResetSignal = (bool)(isdigit (*argv[1]) ||
  1989.                   (!stricmp (argv[1], LIT_exit)) ||
  1990.                   (!stricmp (argv[1], &Trap_ERR[1])) ||
  1991.                   (!stricmp (argv[1], &Trap_DEBUG[1])));
  1992.  
  1993.     for (i = ResetSignal ? 1 : 2; argv[i] != (char *)NULL; ++i)
  1994.     {
  1995.     n = 0;            /* Set not a real signal        */
  1996.  
  1997. /* Check for ERR.  cp points to the variable name */
  1998.  
  1999.     if (!stricmp (argv[i], &Trap_ERR[1]))
  2000.         cp = Trap_ERR;
  2001.  
  2002. /* Check for DEBUG */
  2003.  
  2004.     else if (!stricmp (argv[i], &Trap_DEBUG[1]))
  2005.         cp = Trap_DEBUG;
  2006.  
  2007. /* Check for EXIT */
  2008.  
  2009.     else if (!stricmp (argv[i], LIT_exit))
  2010.         cp = "~0";
  2011.  
  2012. /* Generate the variable name for a numeric value */
  2013.  
  2014.     else
  2015.     {
  2016.         *(cp = tval) = '~';
  2017.         itoa ((n = GetSignalNumber (argv[i])), tval + 1, 10);
  2018.  
  2019.         if (n == -1)
  2020.         return 1;
  2021.     }
  2022.  
  2023.     UnSetVariable (cp, TRUE);
  2024.  
  2025. /* Re-define signal processing */
  2026.  
  2027.     if (!ResetSignal)
  2028.     {
  2029.         if (*argv[1] != '\0')
  2030.         {
  2031.         SetVariableFromString (cp, argv[1]);
  2032.  
  2033.         if (n > 0)
  2034.             signal (n, TerminateSignalled);
  2035.         }
  2036.  
  2037.         else if (n > 0)
  2038.         signal (n, SIG_IGN);
  2039.     }
  2040.  
  2041. /* Clear signal processing */
  2042.  
  2043.     else if (InteractiveFlag)
  2044.     {
  2045.         if (n == SIGINT)
  2046.         signal (n, InterruptSignalled);
  2047.  
  2048.         else if (n > 0)
  2049. #ifdef SIGQUIT
  2050.         signal (n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
  2051. #else
  2052.         signal (n, SIG_DFL);
  2053. #endif
  2054.     }
  2055.  
  2056.     else if (n > 0)
  2057.         signal (n, SIG_DFL);
  2058.     }
  2059.  
  2060.     return 0;
  2061. }
  2062.  
  2063. /*
  2064.  * Get a signal number
  2065.  */
  2066.  
  2067. static int near GetSignalNumber (char *s)
  2068. {
  2069.     register int    n;
  2070.  
  2071.     if (((n = GetNumericValue (s)) < 0) || (n >= NSIG))
  2072.     {
  2073.     PrintWarningMessage ("trap: bad signal number\n");
  2074.     n = -1;
  2075.     }
  2076.  
  2077.     return n;
  2078. }
  2079.  
  2080. /* Convert a string to a number */
  2081.  
  2082. int GetNumericValue (char *as)
  2083. {
  2084.     long    value;
  2085.  
  2086.     return ConvertNumericValue (as, &value, 10) ? (int) value : -1;
  2087. }
  2088.  
  2089. /*
  2090.  * BREAK and CONTINUE processing: break/continue [ n ]
  2091.  */
  2092.  
  2093. static int dobreak (int argc, char **argv)
  2094. {
  2095.     if (argc > 2)
  2096.     return UsageError ("break [ count ]");
  2097.  
  2098.     return BreakContinueProcessing (argv[1], BC_BREAK);
  2099. }
  2100.  
  2101. static int docontinue (int argc, char **argv)
  2102. {
  2103.     if (argc > 2)
  2104.     return UsageError ("continue [ count ]");
  2105.  
  2106.     return BreakContinueProcessing (argv[1], BC_CONTINUE);
  2107. }
  2108.  
  2109. static int near    BreakContinueProcessing (register char *NumberOfLevels,
  2110.                      int Type)
  2111. {
  2112.     register Break_C    *Break_Loc;
  2113.     register int    LevelNumber;
  2114.     char        *cType = (Type == BC_BREAK) ? LIT_break : LIT_continue;
  2115.  
  2116.  
  2117.     LevelNumber = (NumberOfLevels == (char *)NULL)
  2118.             ? 1 : GetNumericValue (NumberOfLevels);
  2119.  
  2120.     if (LevelNumber < 0)
  2121.     return PrintWarningMessage (LIT_Emsg, "bad level number", cType,
  2122.                     NumberOfLevels);
  2123.  
  2124. /* If level is invalid - clear all levels */
  2125.  
  2126.     if (LevelNumber <= 0)
  2127.     LevelNumber = 999;
  2128.  
  2129. /* Move down the stack */
  2130.  
  2131.     do
  2132.     {
  2133.     if ((Break_Loc = Break_List) == (Break_C *)NULL)
  2134.         break;
  2135.  
  2136.     Break_List = Break_Loc->NextExitLevel;
  2137.  
  2138.     } while (--LevelNumber);
  2139.  
  2140. /* Check level */
  2141.  
  2142.     if (LevelNumber)
  2143.     return PrintWarningMessage ("sh: bad break/continue level\n");
  2144.  
  2145.     longjmp (Break_Loc->CurrentReturnPoint, Type);
  2146.  
  2147. /* NOTREACHED */
  2148. }
  2149.  
  2150. /*
  2151.  * Exit function: exit [ status ]
  2152.  */
  2153.  
  2154. static int doexit (int argc, char **argv)
  2155. {
  2156.     Break_C    *SShell_Loc = SShell_List;
  2157.  
  2158.     ProcessingEXECCommand = FALSE;
  2159.  
  2160.     if (argc > 2)
  2161.     return UsageError ("exit [ status ]");
  2162.  
  2163. /* Set up error codes */
  2164.  
  2165.     ExitStatus = (argv[1] != (char *)NULL) ? GetNumericValue (argv[1]) : 0;
  2166.  
  2167.     SetVariableFromNumeric (StatusVariable, (long)ExitStatus);
  2168.  
  2169. /* Are we in a subshell.  Yes - do a longjmp instead of an exit */
  2170.  
  2171.     if (SShell_Loc != (Break_C *)NULL)
  2172.     {
  2173.     SShell_List = SShell_Loc->NextExitLevel;
  2174.     longjmp (SShell_Loc->CurrentReturnPoint, ExitStatus);
  2175.     }
  2176.  
  2177. /*
  2178.  * Check for active jobs
  2179.  */
  2180.  
  2181. #ifdef OS2
  2182.     if (!ExitWithJobsActive)
  2183.     {
  2184.     if (NumberOfActiveJobs () && Interactive)
  2185.     {
  2186.         fputs ("You have running jobs.\n", stderr);
  2187.         ExitWithJobsActive = TRUE;
  2188.         return 0;
  2189.     }
  2190.     }
  2191. #endif
  2192.  
  2193.     ExitTheShell (FALSE);
  2194.     return ExitStatus;
  2195. }
  2196.  
  2197. /*
  2198.  * Function return: return [ status ]
  2199.  *
  2200.  * Set exit value and return via a long jmp
  2201.  */
  2202.  
  2203. static int doreturn (int argc, char **argv)
  2204. {
  2205.     Break_C    *Return_Loc = Return_List;
  2206.  
  2207.     if (argc > 2)
  2208.     return UsageError ("return [ value ]");
  2209.  
  2210.     if  (argv[1] != (char *)NULL)
  2211.     SetVariableFromString (StatusVariable, argv[1]);
  2212.  
  2213. /* If the return address is defined - return to it.  Otherwise, return
  2214.  * the value
  2215.  */
  2216.  
  2217.     if (Return_Loc != (Break_C *)NULL)
  2218.     {
  2219.     Return_List = Return_Loc->NextExitLevel;
  2220.     longjmp (Return_Loc->CurrentReturnPoint, 1);
  2221.     }
  2222.  
  2223.     return GetNumericValue (argv[1]);
  2224. }
  2225.  
  2226. /*
  2227.  * Set function:  set [ -/+flags ] [ parameters... ]
  2228.  */
  2229.  
  2230. static int doset (int argc, char **argv)
  2231. {
  2232.     int            i;
  2233.  
  2234. /* Display ? */
  2235.  
  2236.     if (argc < 2)
  2237.     return ListAllVariables (0xffff, TRUE);
  2238.  
  2239. /* Set/Unset a flag ? */
  2240.  
  2241.     ResetGetOptions ();            /* Reset GetOptions        */
  2242.  
  2243.     while ((i = GetOptions (argc, argv, "abcdefghijklmno:pqrstuvwxyz",
  2244.                 GETOPT_PLUS)) != EOF)
  2245.     {
  2246.     switch (i)
  2247.     {
  2248.         case '?':            /* Unknown */
  2249.         return UsageError ("set [ [-|+][switches] ] [ [-|+]o option ] [ parameter=value ] ...");
  2250.  
  2251.         case 'r':
  2252.         return PrintWarningMessage ("set: r switch cannot be changed\n");
  2253.  
  2254.         case 'o':
  2255.         if ((!ChangeInitialisationValue (OptionArgument,
  2256.                             OptionStart == '-')) &&
  2257.             (!ChangeOptionValue (OptionArgument,
  2258.                      (bool)(OptionStart == '-'))))
  2259.             return PrintWarningMessage ("set: -o bad option (%s)\n",
  2260.                         OptionArgument);
  2261.  
  2262.         break;
  2263.  
  2264.         default:
  2265.         if ((i == 'e') && InteractiveFlag)
  2266.             continue;
  2267.  
  2268.         SetClearFlag (i, (bool)(OptionStart == '-'));
  2269.     }
  2270.     }
  2271.  
  2272. /* Check for --, ++, - and +, which we skip */
  2273.  
  2274.     if ((OptionIndex != argc) &&
  2275.     ((!strcmp (argv[OptionIndex], DoubleHypen))        ||
  2276.      (!strcmp (argv[OptionIndex], DoublePlus))        ||
  2277.      (!strcmp (argv[OptionIndex], ShellOptionsVariable))    ||
  2278.      (!strcmp (argv[OptionIndex], "+"))))
  2279.     OptionIndex++;
  2280.  
  2281. /* Set up parameters ? */
  2282.  
  2283.     if (OptionIndex != argc)
  2284.     {
  2285.     ResetGetoptsValues (TRUE);
  2286.     return SetUpNewParameterVariables (argv, OptionIndex, argc, "set");
  2287.     }
  2288.  
  2289.     else
  2290.     return 0;
  2291. }
  2292.  
  2293. /*
  2294.  * Print the list of functions: functions [ names ]
  2295.  */
  2296.  
  2297. static int dofunctions (int argc, char **argv)
  2298. {
  2299.     FunctionList    *fp;
  2300.     int            i;
  2301.  
  2302.     if (argc < 2)
  2303.     return PrintAllFunctions ();
  2304.  
  2305.     for (i = 1; argv[i] != (char *)NULL; ++i)
  2306.     {
  2307.     if ((fp = LookUpFunction (argv[i])) != (FunctionList *)NULL)
  2308.         PrintFunction (fp->tree);
  2309.  
  2310.     else
  2311.         PrintWarningMessage ("functions: %s is not a function\n", argv[i]);
  2312.     }
  2313.  
  2314.     return 0;
  2315. }
  2316.  
  2317. /*
  2318.  * History functions - history [-ieds]
  2319.  */
  2320.  
  2321. #ifndef NO_HISTORY
  2322. static int dohistory (int argc, char **argv)
  2323. {
  2324.     int        i;
  2325.  
  2326.     if (!InteractiveFlag)
  2327.     return 1;
  2328.  
  2329.     if (argc < 2)
  2330.     PrintHistory (FALSE, TRUE, 0, GetLastHistoryEvent () + 1, stdout);
  2331.  
  2332.     else
  2333.     {
  2334.     ResetGetOptions ();        /* Reset GetOptions        */
  2335.  
  2336.     while ((i = GetOptions (argc, argv, "sidel", 0)) != EOF)
  2337.     {
  2338.         switch (i)
  2339.         {
  2340.         case 's':
  2341.             DumpHistory ();
  2342.             break;
  2343.  
  2344.         case 'i':
  2345.             ClearHistory ();
  2346.             break;
  2347.  
  2348.         case 'l':
  2349.             LoadHistory ();
  2350.             break;
  2351.  
  2352.         case 'd':
  2353.             HistoryEnabled = FALSE;
  2354.             break;
  2355.  
  2356.         case 'e':
  2357.             HistoryEnabled = TRUE;
  2358.             break;
  2359.  
  2360.         default:
  2361.             return UsageError ("history [ -iedsl ]");
  2362.         }
  2363.     }
  2364.     }
  2365.  
  2366.     return 0;
  2367. }
  2368. #endif
  2369.  
  2370. /*
  2371.  * Type function: type [ command ]
  2372.  *
  2373.  * For each name, indicate how it would be interpreted
  2374.  */
  2375.  
  2376. static int dowhence (int argc, char **argv)
  2377. {
  2378.     char        *cp;
  2379.     int            n;            /* Argument count    */
  2380.     int            inb;            /* Inbuilt function    */
  2381.     bool        v_flag;
  2382.     bool        p_flag = FALSE;
  2383.     char        *l_path;
  2384.     FunctionList    *fops;
  2385.     AliasList        *al;
  2386.  
  2387. /* Get some memory for the buffer */
  2388.  
  2389.     if ((l_path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL)
  2390.     return doOutofMemory (*argv);
  2391.  
  2392.     v_flag = (bool)(strcmp (*argv, LIT_type) == 0);
  2393.  
  2394.     ResetGetOptions ();            /* Reset GetOptions        */
  2395.  
  2396.     while ((n = GetOptions (argc, argv, "pv", 0)) != EOF)
  2397.     {
  2398.     switch (n)
  2399.     {
  2400.         case 'v':    v_flag = TRUE;    break;
  2401.         case 'p':    p_flag = TRUE;    break;
  2402.  
  2403.         default:
  2404.         return UsageError (BuiltinUsage);
  2405.     }
  2406.     }
  2407.  
  2408. /* Process each parameter */
  2409.  
  2410.     while ((cp = argv[OptionIndex++]) != (char *)NULL)
  2411.     {
  2412.  
  2413. /* Check for alias */
  2414.  
  2415.     if ((al = LookUpAlias (cp, FALSE)) != (AliasList *)NULL)
  2416.     {
  2417.         if (v_flag)
  2418.         printf ("%s is %s alias for %s\n", cp,
  2419.             (al->Tracked) ? "a tracked" : "an", al->value);
  2420.  
  2421.         else
  2422.         puts (al->value);
  2423.     }
  2424.  
  2425.  
  2426. /* Check for currently use inbuilt version */
  2427.  
  2428.     else if (!p_flag && IsCommandBuiltIn (cp, &inb) && (inb & BLT_CURRENT))
  2429.     {
  2430.         if (v_flag)
  2431.         printf (LIT_2Strings, cp, ShellInternalCommand);
  2432.  
  2433.         else
  2434.         puts (PATH_TO_UNIX (strlwr (l_path)));
  2435.     }
  2436.  
  2437. /* Check for a function */
  2438.  
  2439.     else if (!p_flag &&
  2440.          ((fops = LookUpFunction (cp)) != (FunctionList *)NULL))
  2441.     {
  2442.         if (v_flag)
  2443.         printf (LIT_2Strings, cp, "is a function");
  2444.  
  2445.         else
  2446.         puts (cp);
  2447.     }
  2448.  
  2449. /* Scan the path for an executable */
  2450.  
  2451.     else if (FindLocationOfExecutable (l_path, cp) != EXTENSION_NOT_FOUND)
  2452.     {
  2453. #ifdef OS2
  2454.         if (!IsHPFSFileSystem (l_path))
  2455.         strlwr (l_path);
  2456. #endif
  2457.  
  2458.         if (v_flag)
  2459.         printf (LIT_3Strings, cp, "is ",
  2460.             PATH_TO_UNIX (strlwr (l_path)));
  2461.  
  2462.         else
  2463.         puts (PATH_TO_UNIX (strlwr (l_path)));
  2464.     }
  2465.  
  2466. /* If not found, check for inbuilt version */
  2467.  
  2468.     else if (!p_flag && (IsCommandBuiltIn (cp, &inb)))
  2469.     {
  2470.         if (v_flag)
  2471.         printf (LIT_2Strings, cp, ShellInternalCommand);
  2472.  
  2473.         else
  2474.         puts (cp);
  2475.     }
  2476.  
  2477.     else if (!p_flag && LookUpSymbol (cp))
  2478.     {
  2479.         if (v_flag)
  2480.         printf (LIT_2Strings, cp, "is a shell keyword");
  2481.  
  2482.         else
  2483.         puts (cp);
  2484.     }
  2485.  
  2486.     else if (v_flag)
  2487.         PrintWarningMessage (LIT_2Strings, cp, NotFound);
  2488.     }
  2489.  
  2490.     return 0;
  2491. }
  2492.  
  2493. /*
  2494.  * Table of internal commands.  Note that this table is sort in alphabetic
  2495.  * order.
  2496.  */
  2497.  
  2498. static struct builtin    builtin[] = {
  2499.     { ".",        dodot,        (BLT_ALWAYS | BLT_CURRENT |
  2500.                      BLT_CENVIRON) },
  2501.     { ":",        dolabel,    (BLT_ALWAYS | BLT_CURRENT |
  2502.                      BLT_CENVIRON) },
  2503.     { "[",        dotest,        BLT_CURRENT },
  2504.     { "[[",        dotest,        (BLT_ALWAYS | BLT_CURRENT |
  2505.                      BLT_CENVIRON | BLT_NOGLOB) },
  2506.     { LIT_alias,    doalias,    (BLT_ALWAYS | BLT_CURRENT |
  2507.                      BLT_CENVIRON | BLT_NOGLOB |
  2508.                      BLT_NOWORDS) },
  2509.     { LIT_break,    dobreak,    (BLT_CURRENT | BLT_CENVIRON) },
  2510.     { LIT_builtin,    dobuiltin,    (BLT_ALWAYS | BLT_CURRENT) },
  2511.     { "cd",        dochdir,    BLT_CURRENT },
  2512.     { LIT_continue,    docontinue,    (BLT_CURRENT | BLT_CENVIRON) },
  2513. #ifdef OS2
  2514.     { "detach",    dodetach,    (BLT_ALWAYS | BLT_CURRENT) },
  2515. #endif
  2516.     { "echo",    doecho,        BLT_CURRENT },
  2517.     { "eval",    doeval,        (BLT_CURRENT | BLT_CENVIRON) },
  2518.     { LIT_exec,    (int (*)(int, char **)) doexec,
  2519.                     (BLT_CURRENT | BLT_CENVIRON) },
  2520.     { LIT_exit,    doexit,        (BLT_CURRENT | BLT_CENVIRON) },
  2521.     { LIT_export,    doexport,    (BLT_CURRENT | BLT_CENVIRON |
  2522.                      BLT_NOGLOB | BLT_NOWORDS) },
  2523. #ifdef OS2
  2524.     { "extproc",    dolabel,    (BLT_CURRENT | BLT_CENVIRON) },
  2525. #endif
  2526.     { "false",    dofalse,    BLT_CURRENT },
  2527.     { "fc",        dofc,        BLT_CURRENT },
  2528.     { "functions",    dofunctions,    (BLT_ALWAYS | BLT_CURRENT) },
  2529.     { "getopts",    dogetopts,    BLT_CURRENT },
  2530. #ifndef NO_HISTORY
  2531.     { LIT_history,    dohistory,    BLT_CURRENT },
  2532. #endif
  2533. #ifdef OS2
  2534.     { "jobs",    dojobs,        BLT_CURRENT },
  2535.     { "kill",    dokill,        BLT_CURRENT },
  2536. #endif
  2537.     { "let",    dolet,        BLT_CURRENT },
  2538.     { "msdos",    domsdos,    BLT_CURRENT },
  2539.     { LIT_print,    doecho,        BLT_CURRENT },
  2540.     { "pwd",    dopwd,        BLT_CURRENT },
  2541.     { LIT_read,    doread,        BLT_CURRENT },
  2542.     { "readonly",    doreadonly,    (BLT_CURRENT | BLT_CENVIRON |
  2543.                      BLT_NOGLOB | BLT_NOWORDS) },
  2544.     { "return",    doreturn,    (BLT_CURRENT | BLT_CENVIRON) },
  2545.     { "set",    doset,        BLT_CURRENT },
  2546.     { LIT_shift,    doshift,    (BLT_CURRENT | BLT_CENVIRON) },
  2547. #ifdef OS2
  2548.     { "start",    dostart,    BLT_CURRENT },
  2549. #endif
  2550.     { "swap",    doswap,        BLT_CURRENT },
  2551.     { "test",    dotest,        BLT_CURRENT },
  2552.     { "trap",    dotrap,        (BLT_CURRENT | BLT_CENVIRON) },
  2553.     { "true",    dolabel,    BLT_CURRENT },
  2554.     { LIT_type,    dowhence,    BLT_CURRENT },
  2555.     { "typeset",    dotypeset,    (BLT_CURRENT | BLT_CENVIRON |
  2556.                      BLT_NOGLOB | BLT_NOWORDS) },
  2557.     { "umask",    doumask,    BLT_CURRENT },
  2558.     { LIT_unalias,    dounalias,    (BLT_ALWAYS | BLT_CURRENT) },
  2559.     { LIT_unfunction,
  2560.             dounset,    BLT_CURRENT },
  2561.     { "unset",    dounset,    BLT_CURRENT },
  2562.     { "ver",    dover,        BLT_CURRENT },
  2563. #ifdef OS2
  2564.     { "wait",    dowait,        BLT_CURRENT },
  2565. #endif
  2566.     { "whence",    dowhence,    BLT_CURRENT },
  2567.     { (char *)NULL,    (int (*)())NULL,    0 }
  2568. };
  2569.  
  2570. /*
  2571.  * Look up a built in command
  2572.  */
  2573.  
  2574. int (*IsCommandBuiltIn (register char *s, int *b))()
  2575. {
  2576.     register struct builtin    *bp;
  2577.     int                res;
  2578.  
  2579. /* Check for change drive */
  2580.  
  2581.     *b = TRUE;
  2582.     if ((strlen (s) == 2) && isalpha (*s) && (*s != '_') && (*(s + 1) == ':'))
  2583.     return dodrive;
  2584.  
  2585. /* Search for command */
  2586.  
  2587.     *b = FALSE;
  2588.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  2589.     {
  2590.     if ((res = stricmp (bp->command, s)) >= 0)
  2591.     {
  2592.         if (res != 0)
  2593.             break;
  2594.  
  2595.         *b = bp->mode;
  2596.         return bp->fn;
  2597.     }
  2598.     }
  2599.  
  2600.     return (int (*)())NULL;
  2601. }
  2602.  
  2603. /*
  2604.  * Builtin - either list builtins or execute it
  2605.  *
  2606.  * builtin [-asd] [ names ]
  2607.  */
  2608.  
  2609. static int dobuiltin (int argc, char **argv)
  2610. {
  2611.     register struct builtin    *bp;
  2612.     int                (*shcom)(int, char **) = (int (*)())NULL;
  2613.     int                ReturnValue = 0;
  2614.     char            *carg;
  2615.     int                mode;
  2616.     int                action = -1;
  2617.     int                i;
  2618.  
  2619.     if (argc < 2)
  2620.     {
  2621.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  2622.         printf (LIT_3Strings, LIT_builtin, bp->command,
  2623.               (bp->mode & BLT_CURRENT) ? " - preferred" : "");
  2624.     return 0;
  2625.     }
  2626.  
  2627. /* Check for changing options */
  2628.  
  2629.     ResetGetOptions ();            /* Reset GetOptions        */
  2630.  
  2631.     while ((i = GetOptions (argc, argv, "sad", 0)) != EOF)
  2632.     {
  2633.     switch (i)
  2634.     {
  2635.         case 's':    action = 0;    break;
  2636.         case 'a':    action = 1;    break;
  2637.         case 'd':    action = 2;    break;
  2638.  
  2639.         default:
  2640.         return UsageError (BuiltinUsage);
  2641.     }
  2642.     }
  2643.  
  2644. /* Check to see if we know about the builtin version */
  2645.  
  2646.     if (action == -1)
  2647.     {
  2648.     if ((shcom = IsCommandBuiltIn (argv[1], &mode)) == (int (*)())NULL)
  2649.     {
  2650.         printf (BasicErrorMessage, argv[1], NotBuiltinCommand);
  2651.         return 1;
  2652.     }
  2653.  
  2654. /* Yes - so execute the builtin version.  Set up the word list correctly */
  2655.  
  2656.     argv++;
  2657.     ReturnValue = (*shcom)(CountNumberArguments (argv), argv);
  2658.     argv--;
  2659.     return ReturnValue;
  2660.     }
  2661.  
  2662. /* Execute the requested functions against the builtin commands */
  2663.  
  2664.     while ((carg = argv[OptionIndex++]) != (char *)NULL)
  2665.     {
  2666.     for (bp = builtin;
  2667.         (bp->command != (char *)NULL) && (stricmp (bp->command, carg) != 0);
  2668.         bp++)
  2669.         continue;
  2670.  
  2671. /* Command is not builtin */
  2672.  
  2673.     if (bp->command == (char *)NULL)
  2674.     {
  2675.         printf (BasicErrorMessage, carg, NotBuiltinCommand);
  2676.         ReturnValue = 1;
  2677.         continue;
  2678.     }
  2679.  
  2680. /* Update on the action */
  2681.  
  2682.     switch (action)
  2683.     {
  2684.         case 0:
  2685.         printf (BasicErrorMessage, carg, (bp->mode & BLT_CURRENT)
  2686.                             ? LIT_builtin : "external");
  2687.         break;
  2688.  
  2689.         case 1:
  2690.         bp->mode |= BLT_CURRENT;
  2691.         break;
  2692.  
  2693.         case 2:
  2694.         if (bp->mode & BLT_ALWAYS)
  2695.             printf (BasicErrorMessage, carg, "always builtin");
  2696.  
  2697.         else
  2698.             bp->mode &= ~BLT_CURRENT;
  2699.  
  2700.         break;
  2701.     }
  2702.     }
  2703.  
  2704.     return ReturnValue;
  2705. }
  2706.  
  2707. /*
  2708.  * Report Usage error
  2709.  */
  2710.  
  2711. static int near    UsageError (char *string)
  2712. {
  2713.     return PrintWarningMessage ("Usage: %s\n", string) + 1;
  2714. }
  2715.  
  2716. /*
  2717.  * Alias command: alias [ -t] [ name [=commands] ]...
  2718.  */
  2719.  
  2720. static int doalias (int argc, char **argv)
  2721. {
  2722.     int            ReturnValue = 0;
  2723.     int            i = 1;
  2724.     bool        tracked = FALSE;
  2725.     char        *path = (char *)NULL;
  2726.     char        *cp;
  2727.  
  2728. /* Check for tracked aliases */
  2729.  
  2730.     if ((argc > 1) && (strcmp (argv[1], "-t") == 0))
  2731.     {
  2732.     ++i;
  2733.     tracked = TRUE;
  2734.     }
  2735.  
  2736. /* List only? */
  2737.  
  2738.     if (argc <= i)
  2739.     return PrintAllAlias (tracked);
  2740.  
  2741. /* Set them up or print them */
  2742.  
  2743.     while (i < argc)
  2744.     {
  2745.  
  2746. /* Not tracked - either set alias value if there is an equals or display
  2747.  * the alias value
  2748.  */
  2749.  
  2750.     if (!tracked)
  2751.     {
  2752.         if ((cp = strchr (argv[i], '=')) != (char *)NULL)
  2753.         *(cp++) = 0;
  2754.  
  2755. /* Check for valid name */
  2756.  
  2757.         if (!IsValidAliasName (argv[i], TRUE))
  2758.         return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
  2759.                         argv[i]);
  2760.  
  2761. /* Save it if appropriate */
  2762.  
  2763.         if (cp != (char *)NULL)
  2764.         {
  2765.         if (!SaveAlias (argv[i], cp, tracked))
  2766.             ReturnValue = 1;
  2767.         }
  2768.  
  2769. /* Print it */
  2770.  
  2771.         else if (LookUpAlias (argv[i], FALSE) == (AliasList *)NULL)
  2772.         {
  2773.         PrintWarningMessage (NotAnAlias, LIT_alias, argv[i]);
  2774.         ReturnValue = 1;
  2775.         }
  2776.  
  2777.         else
  2778.         PrintAlias (argv[i]);
  2779.     }
  2780.  
  2781. /* Set up tracked alias */
  2782.  
  2783.     else if (!IsValidAliasName (argv[i], TRUE))
  2784.         return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
  2785.                     argv[i]);
  2786.  
  2787.     else if ((path == (char *)NULL) &&
  2788.          ((path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL))
  2789.         return doOutofMemory (*argv);
  2790.  
  2791. /* Save the new path for the alias */
  2792.  
  2793.     else if (SaveAlias (argv[i],
  2794.                 (FindLocationOfExecutable (path, argv[i])
  2795.                 != EXTENSION_NOT_FOUND) ? path : null, tracked))
  2796.         ReturnValue = 1;
  2797.  
  2798.     ++i;
  2799.     }
  2800.  
  2801.     return ReturnValue;
  2802. }
  2803.  
  2804. /*
  2805.  * UnAlias command: unalias name...
  2806.  */
  2807.  
  2808. static int dounalias (int argc, char **argv)
  2809. {
  2810.     AliasList        *al;
  2811.     int            i;
  2812.  
  2813. /* Set them up or print them */
  2814.  
  2815.     for (i = 1; i < argc; ++i)
  2816.     {
  2817.     if ((al = LookUpAlias (argv[i], FALSE)) != (AliasList *)NULL)
  2818.         DeleteAlias (argv[i]);
  2819.  
  2820.     else
  2821.         PrintWarningMessage (NotAnAlias, LIT_unalias, argv[i]);
  2822.     }
  2823.  
  2824.     return 0;
  2825. }
  2826.  
  2827. /*
  2828.  * OS2 detach function
  2829.  */
  2830.  
  2831. #ifdef OS2
  2832. static int    dodetach (int argc, char **argv)
  2833. {
  2834.     int        RetVal;
  2835.  
  2836.     if (argc < 2)
  2837.     return UsageError ("detach program [ parameters ... ]");
  2838.  
  2839.     argv++;
  2840.     RetVal = ExecuteACommand (argv, EXEC_SPAWN_DETACH);
  2841.     argv--;
  2842.     return RetVal;
  2843. }
  2844.  
  2845. /*
  2846.  * Start a session
  2847.  */
  2848.  
  2849. static int dostart (int argc, char **argv)
  2850. {
  2851.     bool    Direct = FALSE;
  2852.     bool    UseCMD = FALSE;
  2853.     STARTDATA    stdata;
  2854.     Word_B    *wb = (Word_B *)NULL;
  2855.     int        c;
  2856.  
  2857. /* Initialise the session control info */
  2858.  
  2859.     stdata.FgBg = FALSE;        /* Set background session    */
  2860.     stdata.Length = sizeof (STARTDATA);
  2861.     stdata.PgmTitle = (char *)NULL;
  2862.     stdata.Related = FALSE;
  2863.     stdata.TraceOpt = 0;
  2864.     stdata.TermQ = 0;
  2865.     stdata.InheritOpt = 0;
  2866.     stdata.IconFile = (char *)NULL;
  2867.     stdata.PgmHandle = 0L;
  2868.     stdata.PgmControl = 8;
  2869.     stdata.InitXPos = 0;
  2870.     stdata.InitYPos = 0;
  2871.     stdata.InitXSize = 100;
  2872.     stdata.InitYSize = 100;
  2873.  
  2874. /* These values get reset somewhere along the line */
  2875.  
  2876.     stdata.SessionType = 2;
  2877.     stdata.Environment = (char *)1;    /* Build the environment */
  2878.  
  2879. /* Switch on the arguments */
  2880.  
  2881.     ResetGetOptions ();            /* Reset GetOptions        */
  2882.  
  2883.     while ((c = GetOptions (argc, argv, "t:dfWPFiC", 0)) != EOF)
  2884.     {
  2885.     switch (c)
  2886.     {
  2887.         case 't':            /* Set title            */
  2888.         stdata.PgmTitle = OptionArgument;
  2889.  
  2890.         if (strlen (OptionArgument) > 32)
  2891.             OptionArgument[33] = 0;
  2892.  
  2893.         break;
  2894.  
  2895.         case 'd':            /* Direct             */
  2896.         Direct = TRUE;
  2897.         break;
  2898.  
  2899.         case 'f':            /* Foreground            */
  2900.         stdata.FgBg = TRUE;
  2901.         break;
  2902.  
  2903.         case 'W':            /* PM Window            */
  2904.             stdata.SessionType = 2;
  2905.         break;
  2906.  
  2907.         case 'P':            /* PM Session            */
  2908.             stdata.SessionType = 3;
  2909.         break;
  2910.  
  2911.         case 'F':            /* Full Screen            */
  2912.             stdata.SessionType = 1;
  2913.         break;
  2914.  
  2915.         case 'C':            /* Use the CMD processor    */
  2916.         UseCMD = TRUE;
  2917.         break;
  2918.  
  2919.         case 'i':            /* Inherit environment        */
  2920.         stdata.Environment = (char *)NULL;
  2921.         break;
  2922.  
  2923.         default:
  2924.         return UsageError ("start [ -dfWPFiC ] [ -t title ] [ program args..]");
  2925.     }
  2926.     }
  2927.  
  2928. /* Find the program to start */
  2929.  
  2930.     if ((OptionIndex == argc) || (!Direct))
  2931.     {
  2932.     if (!UseCMD)
  2933.         wb = AddWordToBlock (GetVariableAsString (ShellVariableName,
  2934.                                   FALSE), wb);
  2935.  
  2936.     else
  2937.     {
  2938.         wb = AddWordToBlock (GetVariableAsString (ComspecVariable,
  2939.                               FALSE), wb);
  2940.         wb = AddWordToBlock ("/c", wb);
  2941.     }
  2942.     }
  2943.  
  2944.     else
  2945.     wb = AddWordToBlock (argv[OptionIndex++], wb);
  2946.  
  2947.     SessionControlBlock = &stdata;
  2948.  
  2949. /* Build the argument block */
  2950.  
  2951.     while (OptionIndex != argc)
  2952.     wb = AddWordToBlock (argv[OptionIndex++], wb);
  2953.  
  2954. /* Start the session */
  2955.  
  2956.     argv = GetWordList (AddWordToBlock (NOWORD, wb));
  2957.  
  2958.     return (ExecuteACommand (argv, 0) == -1) ? 1 : 0;
  2959. }
  2960. #endif
  2961.  
  2962.  
  2963. /*
  2964.  * Set up new Parameter Variables
  2965.  */
  2966.  
  2967. static int near SetUpNewParameterVariables (char **Array, int Offset, int Max,
  2968.                         char *function)
  2969. {
  2970.     int        n;
  2971.     Word_B    *wb = (Word_B *)NULL;
  2972.     char    *cp;
  2973.     bool    Duplicate = (bool)(strcmp (function, LIT_shift) == 0);
  2974.  
  2975.     if ((wb = AddParameter (ParameterArray[0], wb, function)) == (Word_B *)NULL)
  2976.     return 1;
  2977.  
  2978.     for (n = Offset; n < Max; ++n)
  2979.     {
  2980.     if ((cp = (Duplicate) ? StringSave (Array[n]) : Array[n])
  2981.         == null)
  2982.         return doOutofMemory (function);
  2983.  
  2984.     if ((wb = AddParameter (cp, wb, function)) == (Word_B *)NULL)
  2985.         return 1;
  2986.     }
  2987.  
  2988.     return (AddParameter ((char *)NULL, wb, function) == (Word_B *)NULL)
  2989.         ? 1 : 0;
  2990. }
  2991.  
  2992. /*
  2993.  * dolet - Each arg is an arithmetic expression to be evaluated.
  2994.  */
  2995.  
  2996. static int dolet (int argc, char **argv)
  2997. {
  2998.     long    Value = 0;
  2999.     int        i;
  3000.  
  3001.     ExpansionErrorDetected = FALSE;
  3002.  
  3003.     for (i = 1; !ExpansionErrorDetected && (argv[i] != (char *)NULL); ++i)
  3004.     Value = EvaluateMathsExpression (argv[i]);
  3005.  
  3006.     return !Value || ExpansionErrorDetected ? 1 : 0;
  3007. }
  3008.  
  3009. /*
  3010.  * Out of memory error
  3011.  */
  3012.  
  3013. static int near doOutofMemory (char *s)
  3014. {
  3015.     return PrintWarningMessage (BasicErrorMessage, s, Outofmemory1);
  3016. }
  3017.  
  3018. /*
  3019.  * MSDOS, EXPORT and READONLY functions: xxxx [ variable names... ]
  3020.  */
  3021.  
  3022. static int doexport (int argc, char **argv)
  3023. {
  3024.     return UpdateVariableStatus (argv + 1, STATUS_EXPORT);
  3025. }
  3026.  
  3027. static int doreadonly (int argc, char **argv)
  3028. {
  3029.     return UpdateVariableStatus (argv + 1, STATUS_READONLY);
  3030. }
  3031.  
  3032. static int domsdos (int argc, char **argv)
  3033. {
  3034.     return UpdateVariableStatus (argv + 1, STATUS_CONVERT_MSDOS);
  3035. }
  3036.  
  3037. static int near UpdateVariableStatus (char **argv, unsigned int Mask)
  3038. {
  3039.     if (*argv == (char *)NULL)
  3040.         return ListAllVariables (Mask, FALSE);
  3041.  
  3042.     else
  3043.     {
  3044.     memset (&TypesetValues, 0, sizeof (TypesetValues));
  3045.     TypesetValues.Flags_On = Mask;
  3046.     return TypesetVariables (argv);
  3047.     }
  3048. }
  3049.  
  3050. /*
  3051.  * Sort Compare function for Environment variables.
  3052.  */
  3053.  
  3054. static int ComparisonForEnvironmentSort (VariableList **s1, VariableList **s2)
  3055. {
  3056.     return strcmp ((*s1)->name, (*s2)->name);
  3057. }
  3058.  
  3059. /*
  3060.  * List All variables matching a STATUS
  3061.  */
  3062.  
  3063. static int near ListAllVariables (unsigned int Mask, bool PrintValue)
  3064. {
  3065.     HandleSECONDandRANDOM ();
  3066.  
  3067.     DVE_Mask = Mask;
  3068.     DVE_PrintValue = PrintValue;
  3069.  
  3070.     twalk (VariableTree, DisplayVariableEntry);
  3071.     return 0;
  3072. }
  3073.  
  3074. /*
  3075.  * TWALK Function - display VARIABLE tree
  3076.  */
  3077.  
  3078. static void DisplayVariableEntry (void *key, VISIT visit, int level)
  3079. {
  3080.     VariableList    *vp = *(VariableList **)key;
  3081.  
  3082.     if ((visit == postorder) || (visit == leaf))
  3083.     {
  3084.     if ((isalpha (*vp->name)) &&
  3085.         ((vp->status & DVE_Mask) ||
  3086.              ((vp->status == 0) && (DVE_Mask == 0xffff))))
  3087.         PrintEntry (vp, DVE_PrintValue,
  3088.             (DVE_Mask == 0xffff) ? 0 : DVE_Mask);
  3089.     }
  3090. }
  3091.  
  3092. /*
  3093.  * typeset function - [ [ [+-][Hflprtux] ] [+-][LRZi[n]] [ name [=value] ...]
  3094.  */
  3095.  
  3096. static int dotypeset (int argc, char **argv)
  3097. {
  3098.     int            ReturnValue = 0;
  3099.     bool        f_flag = FALSE;
  3100.     unsigned int    *CurrentFlags;
  3101.     int            tmp = 0;
  3102.     char        c_opt;
  3103.     char        *cp;
  3104.  
  3105. /* Initialise save area */
  3106.  
  3107.     memset (&TypesetValues, 0, sizeof (TypesetValues));
  3108.     OptionIndex = 1;
  3109.  
  3110. /* Scan the options */
  3111.  
  3112.     while ((cp = argv[OptionIndex]) != (char *)NULL)
  3113.     {
  3114.     if ((*cp != '-') && (*cp != '+'))
  3115.         break;
  3116.  
  3117.     CurrentFlags = (*cp == '-') ? &TypesetValues.Flags_On
  3118.                     : &TypesetValues.Flags_Off;
  3119.  
  3120.     while (*(++cp))
  3121.     {
  3122.         switch (*cp)
  3123.         {
  3124.         case 'p':
  3125.             fprintf (stderr, "typeset: Option (%c) not supported\n",
  3126.                  *cp);
  3127.             break;
  3128.  
  3129.         case 'H':
  3130.             *CurrentFlags |= STATUS_CONVERT_MSDOS;
  3131.             break;
  3132.  
  3133.         case 'f':        /* Function only        */
  3134.             f_flag = TRUE;
  3135.             break;
  3136.  
  3137.         case 'l':
  3138.             *CurrentFlags |= STATUS_LOWER_CASE;
  3139.             break;
  3140.  
  3141.         case 'r':
  3142.             *CurrentFlags |= STATUS_READONLY;
  3143.             break;
  3144.  
  3145.         case 't':
  3146.             *CurrentFlags |= STATUS_TAGGED;
  3147.             break;
  3148.  
  3149.         case 'u':
  3150.             *CurrentFlags |= STATUS_UPPER_CASE;
  3151.             break;
  3152.  
  3153.         case 'x':
  3154.             *CurrentFlags |= STATUS_EXPORT;
  3155.             break;
  3156.  
  3157.         case 'L':
  3158.         case 'R':
  3159.         case 'Z':
  3160.         case 'i':
  3161.         {
  3162.             switch (c_opt = *cp)
  3163.             {
  3164.             case 'L':
  3165.                 *CurrentFlags |= STATUS_LEFT_JUSTIFY;
  3166.                 break;
  3167.  
  3168.             case 'R':
  3169.                 *CurrentFlags |= STATUS_RIGHT_JUSTIFY;
  3170.                 break;
  3171.  
  3172.             case 'Z':
  3173.                 *CurrentFlags |= STATUS_ZERO_FILL;
  3174.                 break;
  3175.  
  3176.             case 'i':
  3177.                 *CurrentFlags |= STATUS_INTEGER;
  3178.                 break;
  3179.             }
  3180.  
  3181. /* Only set width on on */
  3182.  
  3183.             if (CurrentFlags != &TypesetValues.Flags_On)
  3184.             break;
  3185.  
  3186. /* Check for following numeric */
  3187.  
  3188.             if (isdigit (*(cp + 1)))
  3189.             tmp = (int)strtol (cp + 1, &cp, 10);
  3190.  
  3191.             else if ((*(cp + 1) == 0) &&
  3192.                  (OptionIndex + 1 < argc) &&
  3193.                  isdigit (*argv[OptionIndex + 1]))
  3194.             tmp = (int)strtol (argv[++OptionIndex], &cp, 10);
  3195.  
  3196.             else
  3197.             break;
  3198.  
  3199. /* Check for invalid numeric */
  3200.  
  3201.             if (!tmp || *(cp--))
  3202.             return UsageError (TypeSetUsage);
  3203.  
  3204. /* Width or base */
  3205.  
  3206.             if (c_opt == 'i')
  3207.             TypesetValues.Base = tmp;
  3208.  
  3209.             else
  3210.             TypesetValues.Width = tmp;
  3211.  
  3212.             break;
  3213.         }
  3214.  
  3215.         default:
  3216.             return UsageError (TypeSetUsage);
  3217.         }
  3218.     }
  3219.  
  3220.     ++OptionIndex;
  3221.     }
  3222.  
  3223. /* Check for f flag - function processing. */
  3224.  
  3225.     if (f_flag)
  3226.     {
  3227.     if (((TypesetValues.Flags_On | TypesetValues.Flags_Off) &
  3228.         ~(STATUS_TAGGED | STATUS_EXPORT)) ||
  3229.         (!(TypesetValues.Flags_On | TypesetValues.Flags_Off)))
  3230.         return PrintWarningMessage ("typeset: Only -xt allowed with -f\n");
  3231.  
  3232.     for (tmp = OptionIndex; tmp < argc; tmp++)
  3233.         ReturnValue |= HandleFunction (argv[tmp]);
  3234.  
  3235.     return ReturnValue;
  3236.     }
  3237.  
  3238. /* Process variable assignments */
  3239.  
  3240.     return TypesetVariables (&argv[OptionIndex]);
  3241. }
  3242.  
  3243. static int near TypesetVariables (char **argv)
  3244. {
  3245.     bool        PrintValue = TypesetValues.Flags_Off ? TRUE : FALSE;
  3246.     VariableList    *vp;
  3247.     char        *CurrentName;
  3248.     char        *NewValue;
  3249.     char        *OldValue;
  3250.     int            OldStatus;
  3251.     long        NValue;
  3252.     char        *xp;
  3253.     int            Retval = 0;
  3254.     unsigned int    Mask;
  3255.  
  3256.     if ((Mask = (TypesetValues.Flags_On | TypesetValues.Flags_Off)) == 0)
  3257.     Mask = 0xffff;
  3258.  
  3259. /* Switch off any appropriate flags */
  3260.  
  3261.     if (TypesetValues.Flags_On & STATUS_LOWER_CASE)
  3262.     TypesetValues.Flags_Off |= STATUS_UPPER_CASE;
  3263.  
  3264.     if (TypesetValues.Flags_On & STATUS_UPPER_CASE)
  3265.     TypesetValues.Flags_Off |= STATUS_LOWER_CASE;
  3266.  
  3267.     if (TypesetValues.Flags_On & STATUS_RIGHT_JUSTIFY)
  3268.     TypesetValues.Flags_Off |= STATUS_LEFT_JUSTIFY;
  3269.  
  3270.     if (TypesetValues.Flags_On & STATUS_LEFT_JUSTIFY)
  3271.     TypesetValues.Flags_Off |= STATUS_RIGHT_JUSTIFY;
  3272.  
  3273. /* If no arguments, print all values matching the mask */
  3274.  
  3275.     if (*argv == (char *)NULL)
  3276.     return ListAllVariables (Mask, PrintValue);
  3277.  
  3278. /* Process each argument.  If no flags, print it */
  3279.  
  3280.     while ((CurrentName = *(argv++)) != (char *)NULL)
  3281.     {
  3282.     if ((NewValue = strchr (CurrentName, '=')) != (char *)NULL)
  3283.         *(NewValue++) = 0;
  3284.  
  3285. /* Check for a valid name */
  3286.  
  3287.     if (IsValidVariableName (CurrentName))
  3288.     {
  3289.         Retval = PrintWarningMessage (BasicErrorMessage, CurrentName,
  3290.                       "bad identifier");
  3291.         continue;
  3292.     }
  3293.  
  3294. /* If valid - update, otherwise print a message */
  3295.  
  3296.     if ((Mask != 0xffff) || (NewValue != (char *)NULL))
  3297.     {
  3298.         vp = LookUpVariable (CurrentName, TRUE);
  3299.         OldStatus = vp->status;
  3300.         OldValue = GetVariableAsString (CurrentName, FALSE);
  3301.  
  3302. /* Update status */
  3303.  
  3304.         vp->status &= ~(TypesetValues.Flags_Off);
  3305.         vp->status |= TypesetValues.Flags_On;
  3306.  
  3307.         if (CurrentFunction != (FunctionList *)NULL)
  3308.         vp->status |= STATUS_LOCAL;
  3309.  
  3310. /* Set up a new integer value.  If the variable was not an integer
  3311.  * originally and there is an error, unset it
  3312.  */
  3313.  
  3314.         if (vp->status & STATUS_INTEGER)
  3315.         {
  3316.         if (ValidMathsExpression (xp = (NewValue != (char *)NULL)
  3317.                           ? NewValue : OldValue,
  3318.                        &NValue))
  3319.         {
  3320.             Retval = PrintWarningMessage (LIT_Emsg, "bad numeric value",
  3321.                           CurrentName, xp);
  3322.  
  3323.             if (!(OldStatus & STATUS_INTEGER))
  3324.             UnSetVariable (CurrentName, FALSE);
  3325.  
  3326.             continue;
  3327.         }
  3328.  
  3329. /* Save the new integer value and set up base etc */
  3330.  
  3331.         vp->nvalue = NValue;
  3332.  
  3333.         if (!vp->base)
  3334.             vp->base = (LastNumberBase != -1) ? LastNumberBase : 10;
  3335.  
  3336.         if (TypesetValues.Base)
  3337.             vp->base = TypesetValues.Base;
  3338.  
  3339.         if (vp->value != null)
  3340.             ReleaseMemoryCell ((void *)vp->value);
  3341.  
  3342.         vp->value = null;
  3343.         }
  3344.  
  3345. /* String - update if appropriate, both the value and the width */
  3346.  
  3347.         else
  3348.         {
  3349.         SetVariableFromString (CurrentName,
  3350.                        (NewValue != (char *)NULL) ? NewValue
  3351.                                   : OldValue);
  3352.  
  3353.         }
  3354.  
  3355.         if (TypesetValues.Width)
  3356.         vp->width = TypesetValues.Width;
  3357.     }
  3358.  
  3359. /* Print if appropriate */
  3360.  
  3361.     else
  3362.         PrintEntry (LookUpVariable (CurrentName, FALSE), PrintValue,
  3363.             Mask);
  3364.     }
  3365.  
  3366.     return Retval;
  3367. }
  3368.  
  3369. static void near PrintEntry (VariableList *vp, bool PrintValue,
  3370.                  unsigned int Mask)
  3371. {
  3372.     unsigned int    Flags = vp->status & Mask;
  3373.  
  3374.     if (vp->status & STATUS_NOEXISTANT)
  3375.     return;
  3376.  
  3377.     if (Flags & STATUS_INTEGER)
  3378.     printf ("integer ");
  3379.  
  3380.     if (Flags & STATUS_LEFT_JUSTIFY)
  3381.     printf ("left justified %d ", vp->width);
  3382.  
  3383.     if (Flags & STATUS_RIGHT_JUSTIFY)
  3384.     printf ("right justified %d ", vp->width);
  3385.  
  3386.     if (Flags & STATUS_ZERO_FILL)
  3387.     printf ("zero filled %d ", vp->width);
  3388.  
  3389.     if (Flags & STATUS_CONVERT_MSDOS)
  3390.     printf ("MS-DOS Format ");
  3391.  
  3392.     if (Flags & STATUS_LOWER_CASE)
  3393.     printf ("lowercase ");
  3394.  
  3395.     if (Flags & STATUS_UPPER_CASE)
  3396.     printf ("uppercase ");
  3397.  
  3398.     if (Flags & STATUS_READONLY)
  3399.     printf ("readonly ");
  3400.  
  3401.     if (Flags & STATUS_TAGGED)
  3402.     printf ("tagged ");
  3403.  
  3404.     if (Flags & STATUS_EXPORT)
  3405.     printf ("exported ");
  3406.  
  3407. /* Print the value */
  3408.  
  3409.     if (!PrintValue)
  3410.     puts (vp->name);
  3411.  
  3412.     else
  3413.     printf (ListVarFormat, vp->name, GetVariableAsString (vp->name, TRUE));
  3414. }
  3415.  
  3416. /*
  3417.  * Handle typeset -f
  3418.  */
  3419.  
  3420. static int near HandleFunction (char *name)
  3421. {
  3422.     FunctionList    *fop;
  3423.  
  3424.     if (strchr (name, '=') != (char *)NULL)
  3425.     return PrintWarningMessage ("typeset: cannot assign to functions\n");
  3426.  
  3427.     if ((fop = LookUpFunction (name)) == (FunctionList *)NULL)
  3428.     return PrintWarningMessage ("typeset: function %s does not exist\n",
  3429.                     name);
  3430.  
  3431.     fop->Traced = (TypesetValues.Flags_On & STATUS_TAGGED) ? TRUE : FALSE;
  3432.     return 0;
  3433. }
  3434.  
  3435. /*
  3436.  * Modified version of getopt for shell
  3437.  */
  3438.  
  3439. static void near ResetGetOptions (void)
  3440. {
  3441.     OptionIndex = 1;            /* Reset the optind flag    */
  3442.     GetOptionPosition = 1;        /* Current position    */
  3443. }
  3444.  
  3445. int    GetOptions (int argc, char **argv, char *optstring, int flags)
  3446. {
  3447.     int        cur_option;        /* Current option        */
  3448.     char    *cp;            /* Character pointer        */
  3449.  
  3450.     if (GetOptionPosition == 1)
  3451.     {
  3452.  
  3453. /* Special for doecho */
  3454.  
  3455.     if (flags & GETOPT_PRINT)
  3456.         return EOF;
  3457.  
  3458. /* Check for out of range, correct start character and not single */
  3459.  
  3460.     if ((OptionIndex >= argc) ||
  3461.         (!(((OptionStart = *argv[OptionIndex]) == '-') ||
  3462.            (((flags & GETOPT_PLUS) && (OptionStart == '+'))))) ||
  3463.         (!argv[OptionIndex][1]))
  3464.         return EOF;
  3465.  
  3466.     if ((!strcmp (argv[OptionIndex], DoubleHypen)) ||
  3467.         ((flags & GETOPT_PLUS) &&
  3468.          (!strcmp (argv[OptionIndex], DoublePlus))))
  3469.         return EOF;
  3470.     }
  3471.  
  3472. /* Get the current character from the current argument vector */
  3473.  
  3474.     cur_option = argv[OptionIndex][GetOptionPosition];
  3475.  
  3476. /* Validate it */
  3477.  
  3478.     if ((cur_option == ':') ||
  3479.     ((cp = strchr (optstring, cur_option)) == (char *)NULL))
  3480.     {
  3481.     if (flags & GETOPT_MESSAGE)
  3482.         PrintWarningMessage ("%s: illegal option -- %c\n", argv[0],
  3483.                  cur_option);
  3484.  
  3485. /* Save the bad option value and move to the next offset */
  3486.  
  3487.     BadOptionValue = cur_option;
  3488.  
  3489.     if (!argv[OptionIndex][++GetOptionPosition])
  3490.     {
  3491.         OptionIndex++;
  3492.         GetOptionPosition = 1;
  3493.     }
  3494.  
  3495.     return '?';
  3496.     }
  3497.  
  3498. /* Parameters following ? */
  3499.  
  3500.     OptionArgument = (char *)NULL;
  3501.  
  3502.     if (*(++cp) == ':')
  3503.     {
  3504.     if (argv[OptionIndex][GetOptionPosition + 1])
  3505.         OptionArgument = &argv[OptionIndex++][GetOptionPosition + 1];
  3506.  
  3507.     else if (++OptionIndex >= argc)
  3508.     {
  3509.         if (flags & GETOPT_MESSAGE)
  3510.         PrintWarningMessage ("%s: option (%c) requires an argument\n",
  3511.                      argv[0], cur_option);
  3512.  
  3513.         BadOptionValue = cur_option;
  3514.         OptionArgument = (char *)-1;
  3515.         GetOptionPosition = 1;
  3516.         return '?';
  3517.     }
  3518.  
  3519.     else
  3520.         OptionArgument = argv[OptionIndex++];
  3521.  
  3522.     GetOptionPosition = 1;
  3523.     }
  3524.  
  3525.     else if (!argv[OptionIndex][++GetOptionPosition])
  3526.     {
  3527.     GetOptionPosition = 1;
  3528.     OptionIndex++;
  3529.     }
  3530.  
  3531.     return cur_option;
  3532. }
  3533.  
  3534.  
  3535. #ifdef OS2
  3536. /*
  3537.  * Kill the specified processes
  3538.  */
  3539.  
  3540. static struct SignalList {
  3541.     char    *Name;
  3542.     int        SigVal;
  3543. } SignalList [] = {
  3544.     {"term",    -1 },        {"usr1",    PFLG_A },
  3545.     {"usr2",    PFLG_B },    {"usr3",    PFLG_C },
  3546.     {(char *)NULL,    0 }
  3547. };
  3548.  
  3549. static int dokill (int argc, char **argv)
  3550. {
  3551.     int        i;
  3552.     int        Sigv = -1;
  3553.     char    *cp;
  3554.     PID        pidProcess;
  3555.     long    value;
  3556.  
  3557.     if (argc < 2)
  3558.     return UsageError (KillUsage);
  3559.  
  3560. /* List signals ? */
  3561.  
  3562.     if (!strcmp (argv[1], "-l"))
  3563.     {
  3564.     for (i = 0; SignalList[i].Name != (char *)NULL; ++i)
  3565.         puts (SignalList[i].Name);
  3566.  
  3567.     return 0;
  3568.     }
  3569.  
  3570. /* Look up signal name */
  3571.  
  3572.     if (**(++argv) == '-')
  3573.     {
  3574.     cp = &argv[0][1];
  3575.  
  3576.     for (i = 0; SignalList[i].Name != (char *)NULL; ++i)
  3577.     {
  3578.         if (!stricmp (SignalList[i].Name, cp))
  3579.         break;
  3580.     }
  3581.  
  3582.     if (SignalList[i].Name == (char *)NULL)
  3583.         return PrintWarningMessage ("kill: bad signal name (%s)\n", cp);
  3584.  
  3585.     Sigv = SignalList[i].SigVal;
  3586.  
  3587.     if (*(++argv) == (char *)NULL)
  3588.         return UsageError (KillUsage);
  3589.     }
  3590.  
  3591. /* Kill the processes */
  3592.  
  3593.     while (*argv != (char *)NULL)
  3594.     {
  3595.  
  3596. /* Find the PID */
  3597.  
  3598.         if (((**argv == '%') && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
  3599.         ((**argv != '%') && !ConvertNumericValue (*argv, &value, 0)))
  3600.         return PrintWarningMessage ("kill: bad process/job id (%s)\n",
  3601.                      *argv);
  3602.  
  3603. /* If Process ID, its in value */
  3604.  
  3605.      if (**argv != '%')
  3606.         pidProcess = (PID)value;
  3607.  
  3608. /* Send the signal */
  3609.  
  3610.     if ((Sigv == -1) ? DosKillProcess (DKP_PROCESSTREE, pidProcess)
  3611.              : DosFlagProcess (pidProcess, FLGP_SUBTREE, Sigv, 0))
  3612.         return PrintWarningMessage ("kill: signal to process id (%s) failed\n",
  3613.                      *argv);
  3614.  
  3615.     argv++;
  3616.     }
  3617.  
  3618.     return 0;
  3619. }
  3620.  
  3621. /*
  3622.  * Wait for process to end
  3623.  */
  3624.  
  3625. static int dowait (int argc, char **argv)
  3626. {
  3627.     PID        pidProcess;
  3628.     int        TermStat;
  3629.     int        ReturnValue;
  3630.     long    value;
  3631.  
  3632. /* Check usage */
  3633.  
  3634.     if (argc > 2)
  3635.     return UsageError ("wait [ job ]");
  3636.  
  3637. /* Wait for all jobs ? */
  3638.  
  3639.     if (argc < 2)
  3640.     {
  3641.     TermStat = -1;
  3642.  
  3643. /* Yes - wait until wait returns an error */
  3644.  
  3645.     while ((pidProcess = wait (&ReturnValue)) != -1)
  3646.     {
  3647.         DeleteJob (pidProcess);
  3648.         TermStat = ReturnValue;
  3649.     }
  3650.     }
  3651.  
  3652. /* Wait for a specific process.  Job or PID? */
  3653.  
  3654.     else
  3655.     {
  3656.  
  3657. /* Move to the ID */
  3658.     argv++;
  3659.  
  3660. /* Find the PID */
  3661.  
  3662.         if (((**argv == '%') && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
  3663.         ((**argv != '%') && !ConvertNumericValue (*argv, &value, 0)))
  3664.         return PrintWarningMessage ("wait: bad process/job id (%s)\n",
  3665.                      *argv);
  3666.  
  3667. /* If Process ID, its in value */
  3668.  
  3669.      if (**argv != '%')
  3670.         pidProcess = (PID)value;
  3671.  
  3672. /* Wait for the specified process */
  3673.  
  3674.     if (cwait (&TermStat, pidProcess, WAIT_GRANDCHILD) == -1)
  3675.         return PrintWarningMessage ("wait: Process id (%s) not active\n",
  3676.                     *argv);
  3677.  
  3678.     DeleteJob (pidProcess);
  3679.     }
  3680.  
  3681. /* Convert termination status to return code */
  3682.  
  3683.     if (TermStat == -1)
  3684.     return -1;
  3685.  
  3686.     else if (TermStat & 0x00ff)
  3687.     return TermStat & 0x00ff;
  3688.  
  3689.     else
  3690.     return (TermStat >> 8) & 0x00ff;
  3691. }
  3692.  
  3693. /*
  3694.  * Print the job info
  3695.  */
  3696.  
  3697. static int dojobs (int argc, char **argv)
  3698. {
  3699.     bool    ListMode = FALSE;
  3700.     bool    ListTree = FALSE;
  3701.     pid_t    p = getpid ();
  3702.     long    value;
  3703.     int        i;
  3704.  
  3705. /* List signals ? */
  3706.  
  3707.     ResetGetOptions ();            /* Reset GetOptions        */
  3708.  
  3709.     while ((i = GetOptions (argc, argv, "plP:", 0)) != EOF)
  3710.     {
  3711.         switch (i)
  3712.         {
  3713.         case 'l':
  3714.         ListMode = TRUE;
  3715.         break;
  3716.  
  3717.         case 'p':
  3718.         ListTree = TRUE;
  3719.         break;
  3720.  
  3721.         case 'P':
  3722.         ListTree = TRUE;
  3723.  
  3724.         if (!ConvertNumericValue (OptionArgument, &value, 0))
  3725.             return PrintWarningMessage ("jobs: bad process id (%s)\n",
  3726.                             OptionArgument);
  3727.  
  3728.         p = (pid_t)value;
  3729.  
  3730.         break;
  3731.  
  3732.  
  3733.         default:
  3734.         return UsageError (JobUsage);
  3735.     }
  3736.     }
  3737.  
  3738.     if (ListTree)
  3739.         return PrintProcessTree (p);
  3740.  
  3741. /* Look up job name */
  3742.  
  3743.     return PrintJobs (ListMode);
  3744. }
  3745. #endif
  3746.  
  3747. /*
  3748.  * Change option value
  3749.  */
  3750.  
  3751. static bool near ChangeOptionValue (char *value, bool set)
  3752. {
  3753.     struct SetOptions    *entry = LookUpOptionValue (value);
  3754.  
  3755.     if (entry == (struct SetOptions *)NULL)
  3756.     return FALSE;
  3757.  
  3758.     else if (entry->HasOptionValue)
  3759.     SetClearFlag (entry->FlagValue, set);
  3760.  
  3761.     else if (set)
  3762.     GlobalFlags |= entry->FlagValue;
  3763.  
  3764.     else
  3765.     GlobalFlags &= ~(entry->FlagValue);
  3766.  
  3767.     return TRUE;
  3768. }
  3769.  
  3770. /*
  3771.  * Update shell switches
  3772.  */
  3773.  
  3774. static void near SetClearFlag (int Flag, bool set)
  3775. {
  3776.     if (set)
  3777.     FL_SET (Flag);
  3778.  
  3779.     else
  3780.     FL_CLEAR (Flag);
  3781.  
  3782.     SetShellSwitches ();
  3783. }
  3784.  
  3785. /*
  3786.  * Test an option
  3787.  */
  3788.  
  3789. static int near TestOptionValue (char *value)
  3790. {
  3791.     struct SetOptions    *entry = LookUpOptionValue (value);
  3792.  
  3793.     if (entry == (struct SetOptions *)NULL)
  3794.     {
  3795.     PrintWarningMessage ("%s: unknown option - %s\n", TestProgram, value);
  3796.     longjmp (TestErrorReturn, 1);
  3797.     }
  3798.  
  3799.     else if (entry->HasOptionValue)
  3800.     return (FL_TEST (entry->FlagValue) != 0);
  3801.  
  3802.     else
  3803.     return (GlobalFlags & entry->FlagValue);
  3804. }
  3805.  
  3806. /*
  3807.  * Find an Option entry
  3808.  */
  3809.  
  3810. static struct SetOptions * near LookUpOptionValue (char *value)
  3811. {
  3812.     int        i = 0;
  3813.     char    *cp;
  3814.  
  3815.     while ((cp = SetOptions[i].OptionName) != (char *)NULL)
  3816.     {
  3817.     if (!strcmp (cp, value))
  3818.         return &SetOptions[i];
  3819.  
  3820.     ++i;
  3821.     }
  3822.  
  3823.     return (struct SetOptions *)NULL;
  3824. }
  3825.  
  3826. /*
  3827.  * Get Unit number
  3828.  */
  3829.  
  3830. static int near GetUnitNumber (char *prog)
  3831. {
  3832.     int        Unit;
  3833.  
  3834.     if (((Unit = GetNumericValue (OptionArgument)) < 0) || (Unit > 9))
  3835.     {
  3836.     PrintWarningMessage (LIT_Emsg, LIT_bun, prog, OptionArgument);
  3837.     return -1;
  3838.     }
  3839.  
  3840.     return Unit;
  3841. }
  3842.  
  3843. /*
  3844.  * fc function - fc [-e EditorName] [-nlr] [First [Last]]
  3845.  *         fc -e - [Old=New] [Command]
  3846.  */
  3847.  
  3848. static int dofc (int argc, char **argv)
  3849. {
  3850.     char        *Editor = GetVariableAsString ("FCEDIT", FALSE);
  3851.     bool        n_flag = TRUE;
  3852.     bool        l_flag = FALSE;
  3853.     bool        r_flag = FALSE;
  3854.     int            EventNumber[2];
  3855.     char        *Temp;
  3856.     char        *Change = (char *)NULL;
  3857.     char        *Change1;
  3858.     char        *NewBuffer;
  3859.     char        *NewArg[3];
  3860.     int            i;
  3861.     FILE        *fp;
  3862.  
  3863. /* Check status */
  3864.  
  3865.     if (!Interactive ())
  3866.     return PrintWarningMessage ("fc: only available in interactive mode\n");
  3867.  
  3868.     if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 1)) == (char *)NULL)
  3869.     return doOutofMemory ("fc");
  3870.  
  3871. /* Process options */
  3872.  
  3873.     ResetGetOptions ();            /* Reset GetOptions        */
  3874.  
  3875.     while ((i = GetOptions (argc, argv, "e:nlr", 0)) != EOF)
  3876.     {
  3877.     switch (i)
  3878.     {
  3879.         case 'e':            /* editor name            */
  3880.         Editor = OptionArgument;
  3881.         break;
  3882.  
  3883.         case 'n':    n_flag = FALSE;    break;
  3884.         case 'l':    l_flag = TRUE;    break;
  3885.         case 'r':    r_flag = TRUE;    break;
  3886.  
  3887.         default:
  3888.         return UsageError ("fc [ -e EditorName ] [ -nlr ] [ First [Last]]\n       fc -e - [ Old=New ] [ Command ]");
  3889.     }
  3890.     }
  3891.  
  3892.     argv += OptionIndex;
  3893.     argc -= OptionIndex;
  3894.  
  3895. /* Check for [old=new] */
  3896.  
  3897.     if (argc && ((Change1 = strchr (*argv, '=')) != (char *)NULL))
  3898.     {
  3899.     Change = *(argv++);
  3900.     *(Change1++) = 0;
  3901.     --argc;
  3902.     }
  3903.  
  3904.     if (!l_flag)
  3905.     DeleteLastHistory ();
  3906.  
  3907. /* Get the first and last event number */
  3908.  
  3909.     for (i = 0; i < 2; i++)
  3910.     {
  3911.     EventNumber[i] = 0;
  3912.  
  3913.     if (argc)
  3914.     {
  3915.         EventNumber[i] = (int)strtol (*argv, &Temp, 10);
  3916.  
  3917.         if (*Temp)
  3918.         EventNumber[i] = SearchHistory (*argv);
  3919.  
  3920.         else if (EventNumber[i] < 0)
  3921.         EventNumber[i] += GetLastHistoryEvent () - 1;
  3922.  
  3923.         if (EventNumber[i] <= 0)
  3924.         return PrintWarningMessage ("fc: event <%s> not found\n",
  3925.                         *argv);
  3926.  
  3927.         argv++;
  3928.         --argc;
  3929.     }
  3930.     }
  3931.  
  3932. /* Set up first and last values */
  3933.  
  3934.     i = GetLastHistoryEvent () - 1;
  3935.  
  3936.     if (!EventNumber[0])
  3937.     {
  3938.         if ((EventNumber[0] = (l_flag) ? i - 16 : i) <= 0)
  3939.         EventNumber[0] = 1;
  3940.     }
  3941.  
  3942.     if (!EventNumber[1])
  3943.         EventNumber[1] = (l_flag) ? i : EventNumber[0];
  3944.  
  3945. /* If l - print */
  3946.  
  3947.     if (l_flag)
  3948.     fp = stdout;
  3949.  
  3950.     else if (Editor == null)
  3951.     return PrintWarningMessage ("fc: editor not defined\n");
  3952.  
  3953.     else if ((fp = fopen ((Temp = GenerateTemporaryFileName ()), "w+b"))
  3954.             == (FILE *)NULL)
  3955.     return PrintWarningMessage ("fc: cannot create %s\n", Temp);
  3956.  
  3957. /* Process history */
  3958.  
  3959.     if (!l_flag)
  3960.     n_flag = FALSE;
  3961.  
  3962.     PrintHistory (r_flag, n_flag, EventNumber[0], EventNumber[1], fp);
  3963.  
  3964.     if (l_flag)
  3965.     return 0;
  3966.  
  3967. /* Check that we found some history */
  3968.  
  3969.     if (!ftell (fp))
  3970.     l_flag = TRUE;
  3971.  
  3972.     fclose (fp);
  3973.  
  3974.     if (l_flag)
  3975.     {
  3976.     unlink (Temp);
  3977.     return PrintWarningMessage ("fc: no matches\n");
  3978.     }
  3979.  
  3980. /* Invoke the editor */
  3981.  
  3982.     if (strcmp (Editor, ShellOptionsVariable))
  3983.     {
  3984.     NewArg[0] = Editor;
  3985.     NewArg[1] = Temp;
  3986.     NewArg[2] = (char *)NULL;
  3987.  
  3988.     if (ExecuteACommand (NewArg, 0) == -1)
  3989.     {
  3990.         unlink (Temp);
  3991.         return 1;
  3992.     }
  3993.     }
  3994.  
  3995. /* Now execute it */
  3996.  
  3997.     if ((i = S_open (TRUE, Temp, O_RMASK)) < 0)
  3998.     return PrintWarningMessage ("fc: cannot re-open edit file (%s)\n",
  3999.                     Temp);
  4000.  
  4001.     argc = read (i, NewBuffer, LINE_MAX - 1);
  4002.     S_close (i, TRUE);
  4003.  
  4004.     if (argc <= 0)
  4005.     return (argc == 0) ? 0 : 1;
  4006.  
  4007.     else if (argc >= LINE_MAX - 1)
  4008.     return PrintWarningMessage (FCTooLong);
  4009.  
  4010. /* Strip off trailing EOFs and EOLs */
  4011.  
  4012.     CleanUpBuffer (argc, NewBuffer, 0x1a);
  4013.  
  4014. /* Check for substitution */
  4015.  
  4016.     if (Change == (char *)NULL)
  4017.     strcpy (ConsoleLineBuffer, NewBuffer);
  4018.  
  4019.     else
  4020.     {
  4021.     if ((Temp = strstr (NewBuffer, Change)) == (char *)NULL)
  4022.         return PrintWarningMessage ("fc: string not found");
  4023.  
  4024.     if ((i = strlen (NewBuffer) - strlen (Change) +
  4025.          strlen (Change1)) >= LINE_MAX - 2)
  4026.         return PrintWarningMessage (FCTooLong);
  4027.  
  4028. /* Do the substitution */
  4029.  
  4030.     i = Temp - NewBuffer;
  4031.     strncpy (ConsoleLineBuffer, NewBuffer, i);
  4032.     strcpy (ConsoleLineBuffer + i, Change1);
  4033.     strcat (ConsoleLineBuffer, NewBuffer + strlen (Change) + i);
  4034.     }
  4035.  
  4036.     ReleaseMemoryCell ((void *)NewBuffer);
  4037.  
  4038. /* Tell the user what we've done */
  4039.  
  4040.     puts (ConsoleLineBuffer);
  4041.  
  4042. /* Flag the console driver not to read from the console, but use the
  4043.  * current contents of the ConsoleLineBuffer
  4044.  */
  4045.  
  4046.     UseConsoleBuffer = TRUE;
  4047.     return 0;
  4048. }
  4049.  
  4050. /*
  4051.  * Strip off trailing EOFs and EOLs from console buffer
  4052.  */
  4053.  
  4054. static char near CleanUpBuffer (int length, char *buffer, int eofc)
  4055. {
  4056.     char    *cp = &buffer[length - 1];
  4057.     char    ret;
  4058.  
  4059.     while (length && ((*cp == (char)eofc) || (*cp == CHAR_NEW_LINE)))
  4060.     {
  4061.     length--;
  4062.     cp--;
  4063.     }
  4064.  
  4065.     ret = *(cp + 1);
  4066.     *(cp + 1) = 0;
  4067.     return ret;
  4068. }
  4069.  
  4070. /*
  4071.  * Convert Job ID to process id
  4072.  */
  4073.  
  4074. #ifdef OS2
  4075. static bool near ConvertJobToPid (char *String, PID *pid)
  4076. {
  4077.     long    value;
  4078.     JobList    *jp;
  4079.  
  4080. /* If numeric value, look up the job number */
  4081.  
  4082.     if (ConvertNumericValue (String, &value, 0))
  4083.     jp = LookUpJob ((int)value);
  4084.  
  4085.     else
  4086.     jp = SearchForJob (String);
  4087.  
  4088.     if (jp == (JobList *)NULL)
  4089.     return FALSE;
  4090.  
  4091.     PreviousJob = CurrentJob;
  4092.     CurrentJob = jp->pid;
  4093.  
  4094.     *pid = jp->pid;
  4095.     return TRUE;
  4096. }
  4097. #endif
  4098.